Related Contracts

With this in mind:

Would this be considered an anti pattern?:

template A
  with
    p : Party
  where
    signatory p
    nonconsuming choice CreateB : ContractId B
      controller p
      do 
        create B with
           a = this
        
template B
  with
    a : A
  where
    signatory (signatory a)

I suppose it could be refactored like this?

template A2
  with
    unique : Text
    p : Party
  where
    key (p, unique) : (Party, Text)
    maintainer key._1
    signatory p
    nonconsuming choice CreateB2 : ContractId B2
      controller p
      do 
        create B2 with
           a2Id = (this.p, this.unique)
        
template B2
  with
    a2Id : (Party, Text)
  where
    signatory a2Id._1

Or ever CreateB2 could be replaced by a separate API call IE:

demo : Script()
demo = script do
  alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
  submit alice do
    createCmd A2 with
      unique = "UNIQUE"
      p = alice
  
  submit alice do
    createCmd B2 with
      a2Id = (alice, "UNIQUE")
  pure()

This last method might create the scenario where you have instances of B2 that don’t actually refer to any existing A2s, however this might be ok for some use cases.

Given all this, what is the best approach?

@tiw
I would suggest first clearly defining the workflow you’d like to facilitate. Based on the example you quoted at the beginning of your post, I suppose the workflow you’re interested in can be defined as follows. Given the contract for a department you’d like to be able to fetch from the ledger the contract for associated organization. This capability should persist when the data in the organization (e.g. the organization name) is modified (which in Daml means archiving the organization contract and recreating it with new values for the fields).

If this is indeed the workflow you’re after, your first example (with templates A and B respectively representing organization and department) cannot facilitate it. The template B stores the data in the contract created from template A, but it doesn’t store any reference to the contract. Suppose we modify template A by adding the field “orgname”. Then we create a contract from template A with orgname = “BadlySpelledOrgName”. Then we exercise CreateB choice on this contract. The resulting contract B will contain the data from contract A including the value of orgname field set to “BadlySpelledOrgName”. Now suppose we archive contract A and recreate it with orgname = “CorrectlySpelledOrgName”. Now the data in contract B and contract A are out of sync. In this design, if you have contract B, you have no means of fetching contract A from the ledger because contract B has no reference to contract A. All it has is the data (the values of the fields) that contract A had at the time CreateB choice was exercised.

In your second example you do have a link between contract B and contract A in the form of a key from contract A stored in contract B. Since contract key provides a persistent link to the contract, given contract B you can always fetch associated contract A using the key. In other words, the second example can very well facilitate the desired workflow.

In both examples it’s perfectly possible to create contract B without associated contract A. If this is undesirable for your workflow, to prevent this you would need to introduce requirement for additional authority.

1 Like