Dan Haywood created ISIS-1198:
---------------------------------

             Summary: 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.10.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)

Reply via email to