You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@isis.apache.org by "Dan Haywood (JIRA)" <ji...@apache.org> on 2016/01/04 11:19:39 UTC

[jira] [Updated] (ISIS-1198) [DOC] Update website with FAQ articles on contributions and also aggregate roots vs "flat" pattern

     [ https://issues.apache.org/jira/browse/ISIS-1198?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Dan Haywood updated ISIS-1198:
------------------------------
    Summary: [DOC] Update website with FAQ articles  on contributions and also aggregate roots vs "flat" pattern  (was: Update website with FAQ articles  on contributions and also aggregate roots vs "flat" pattern)

> [DOC] Update website with FAQ articles  on contributions and also aggregate roots vs "flat" pattern
> ---------------------------------------------------------------------------------------------------
>
>                 Key: ISIS-1198
>                 URL: https://issues.apache.org/jira/browse/ISIS-1198
>             Project: Isis
>          Issue Type: Documentation
>          Components: Core
>    Affects Versions: 1.9.0
>            Reporter: Dan Haywood
>            Assignee: Dan Haywood
>            Priority: Minor
>             Fix For: 1.12.0
>
>
> 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"
>     



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)