[ https://issues.apache.org/jira/browse/ISIS-1198?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Daniel Keir Haywood updated ISIS-1198: -------------------------------------- Component/s: (was: Isis Core) Isis Docs & Website Fix Version/s: (was: 2.9.0) Summary: FAQ articles on contributions and also aggregate roots vs "flat" pattern (was: [DOC] Update website with FAQ articles on contributions and also aggregate roots vs "flat" pattern) > 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: Isis Docs & Website > Affects Versions: 1.9.0 > Reporter: Daniel Keir Haywood > Priority: Minor > > 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 (v8.3.4#803005)