Uploaded image for project: 'Causeway'
  1. Causeway
  2. CAUSEWAY-1198

FAQ articles on contributions and also aggregate roots vs "flat" pattern

    XMLWordPrintableJSON

Details

    • Documentation
    • Status: Open
    • Minor
    • Resolution: Unresolved
    • 1.9.0
    • None
    • Docs & Website
    • None

    Description

      Uses of Contributions.

      1. to decouple modules

      sub use case (a)

      example: Documents can be contributed to multiple entities, eg:

      • Party
      • Lease

      naive implementation (without contributions ... we DON'T want to do this):

      • Party#getDocuments()
      • Lease#getDocuments()

      instead, we want Party to not know that there are documents associcated with it; ditto for Lease etc.

      on the other hand Document knows that it contributes to other entities:

      simple case: Document knows the actual type of the entities that it contributes to..

      .. thus:

      class DocumentOnPartyContributions {
      List<Document> getDocuments(Party party)

      { ... } // would delegate to the DocumentRepository
      }

      class DocumentOnLeaseContributions {
      List<Document> getDocuments(Lease lease) { ... }


      }

      the consequence of this is that the "document" module depends on the other modules ("party", "lease" etc).

      sub-usecase (b): using interfaces to invert dependencies

      what if instead we are happy for the dependency to go the "other" way, eg "party" does know about "document"

      ... could always go back to the original "naive" impl of Party#getDocuments()

      ... or, could introduce DocumentHolder and have Party implement etc, then use a poly query

      NOT REALLY SURE THIS BUYS VERY MUCH AT ALL (other than introducing the notion of a "DocumentHolder", however that is equally supported just by having:
      public interface DocumentHolder

      { List<Document> getDocuments(); }

      and the naive implementation trivially implements this.

      sub-use case (c):
      what if we DON'T want the document module to know about the party, and equally we don't want party to know about documents?

      so here, we use poly addons

      document — (document, party) — party
      document — (document, lease) — lease

      ^^^
      this tuple is the DocumentLink, and "table of two halves" means that there are implementations for each subtype

      document party lease
      ^ ^ ^
      — assembly module -----

      public interface DocumentLink<T> {
      Document getDocument();
      T getDocumentHolder();
      }

      the tuple implementations are neither in the document nor the party/lease modules, instead they are in a module that depends on both and "binds" them together... it's a module whose responsibility is to assemble the various decoupled modules into a working appication.

      @PersistenceCapable(NEW_TABLE)
      public abstract class DocumentLinkAbstract<T> implements DocumentLink<T> {
      Document getDocument()

      { // concrete }
      }

      @PersistenceCapable(NEW_TABLE)
      public class DocumentLinkForParty extends DocumentLinkAbstract<Party> {
      Party getDocumentHolder() { // concrete }

      }

      per the poly pattern, we need a subscriber for each holder type to manage these tuples...

      public class DocumentLinkForPartySubscriptions

      { ... }


      to make this visible in the UI

      public class DocumentLinkForPartyContributions {

      List<Document> getDocuments(Party party) { ... }

      // follows the link to the actual Document
      }

      there are two ways to "follow the link", either:

      • server-side, ie a JDO query joining DocumentLinkForParty / DocumentLink / Document
      • client-side, ie a JDO query to find all the lnks, then do a Guava transform to obtain corresponding Document (1+N perf)

      ~~~~~~~~~~~~~~

      2. for symmetric relationships where uncertain which "side" should own the behaviour

      related things to say:

      • about whether eg BudgetItem should be exposed at all as its own aggregate root
      • ubiquituous language
      • else, is a Budget and a qualifier (ie Charge, in this case)
      • if not exposed, whether to have a BudgetItemRepository (more generally, repositories for leaf objects)
      • depends on the cardinality (number of instances) of the assocation
      • if small number (<30), then just a regular collection
      • if many, then provide an repository as an internal implementation detail of the aggregate root (Budget)

      flat pattern vs aggregate root

      • don't sweat it
      • if there's only one route to create the "leaf", then, yes, put it onto the root
      • but otherwise, no probs in pulling out a contributions service to let the leaf be created from different directions
      • newBudgetItem(Budget, Charge)
      • within a single module, if there's only ONE parameter being contributed to, then just inline
      • but if two or more, then pull out as a separate contribution so that the system is flexible and does force context
      • "problem solver > process follower"

      Attachments

        Activity

          People

            Unassigned Unassigned
            danhaywood Daniel Keir Haywood
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated: