Hi Dan:

You are welcome! 
It's a valuable work for me to take time thinking in code. It's helpful to make 
a domain driven design using isis.

Best Regards

Alain

-----邮件原件-----
发件人: Dan Haywood [mailto:[email protected]] 
发送时间: 2013年4月18日 14:53
收件人: dev; Vaughn Vernon
主题: Re: A heavier or smaller root aggregate

Hi Alain,
Thanks for taking the time to do this.  Comments within.

Vaughn... this thread relates to some advice in one of your articles.  I've 
cc'ed you in case you want to commentate; as you'll see, I don't believe it 
makes sense in the case of using an ORM (as Isis does).



On 18 April 2013 07:27, 张峰昌 <[email protected]> wrote:

> Hi Dan:
>
> I have tried build two style domain model with scrum management 
> application as business background, and the points I have made are 
> more clear than before.
> Domain Concept include:Product、BacklogItem、Release、Sprint、Task etc. A 
> product have many BacklogItems、Releases、Sprints.
>


> 1、Large business boundary is #1 reason that make A heavy style root 
> aggregate. It's Business boundary makes root aggregate heavy or 
> smaller instead of IdentityType(Datastore or Application).
>  For example Product as the only one root aggregate.Product is so 
> heavy if we model Product as the only root aggregate,because it needs 
> many collections such BacklogItems、Sprints、Releases.
>


Agreed, we should disregard this. The interesting bit of the discussion is the 
next one.



> 2、Direct root aggregate Object Referenced maybe the #2 reason that 
> make A heavy root aggregate.
>  For example,Product、BacklogItem、Release、Sprint are all root 
> aggregate.The relationships between root aggregates have two styles:
>  (1)、Direct root aggregate Object Referenced.  BacklogItem, Release 
> and Sprint will keep a property Product(type:Product) object reference 
> to represent the relationship with Product(root aggregate)
>  (2)、Root aggregate ID Referenced. BacklogItem,Release and Sprint will 
> keep a property ProductID(type:Long) reference to represent the 
> relationship with Product(root aggregate).This technique is called 
> disconnected domain model in Vaughn Vernon's 
> paper(https://vaughnvernon.co/?p=139 ).
>


The PDF of this paper being:[1]

The example code sample that you give below correctly explains Vaughn's advice, 
but I don't agree with it.

For the benefit of others reading this thread, Vaughn says:

*Prefer references to external aggregates only by their globally unique 
identifier, not by holding a direct object reference (pointer)*

The reason for doing this, he says, is:

*Aggregates with inferred object references are thus automatically smaller 
because references are never eagerly loaded.*

and he also says:

*...the disconnected domain model is actually a form of lazy loading...*
*
*
which he then says provides:

*... the additional benefits [of] smaller aggregates better performing models, 
scalabiliity and distribution.*

As I say, I don't agree with this.

Vaughn's advice would make sense if we were using Java serialization, but we're 
not; we use JDO.  If you look at how JDO works, behind the scenes it holds an 
id, and the reference is loaded lazily.  This page [2] explains the mechanics.

So, in terms of the state, the object is the same size; it holds an Id.

As for scalability, well, (as Maurizio has demonstrated), JDO can be used on 
GAE whose implementation is a scalable NoSQL store.  So we have that covered.

As I see it, this disconnected domain model pattern arises from using 
inadequate  non-ORM persistence mechanisms.  But it doesn't make sense if using 
an ORM.  Using it we end up with a much more inconvenient model to work with, 
for no benefit.

I know Vaughn from the DDD mailing list community, so I've cc'ed him on this 
for his reaction if he wishes.

Cheers
Dan

[1]
http://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_2.pdf
[2] http://db.apache.org/jdo/enhancement.html



> Code Sample for  (1)、Direct root aggregate Object Referenced.
> Product.java
> @PersistenceCapable(identityType=IdentityType.DATASTORE)
> public class Product {
>         // {{ Description (property)
>         private String description;
>
>         @MemberOrder(sequence = "1")
>         @Named("description")
>         public String getDescription() {
>                 return description;
>         }
>
>         public void setDescription(final String description) {
>                 this.description = description;
>         }
>         // }}
> }
>
> BacklogItem.java
> @PersistenceCapable(identityType=IdentityType.DATASTORE)
> public class BacklogItem {
>         // {{ Description (property)
>         private String description;
>
>         @MemberOrder(sequence = "1")
>         public String getDescription() {
>                 return description;
>         }
>
>         public void setDescription(final String description) {
>                 this.description = description;
>         }
>         // }}
>
>         // {{ Product (property)
>         private Product product;
>
>         @MemberOrder(sequence = "2")
>         public Product getProduct() {
>                 return product;
>         }
>
>         public void setProduct(final Product product) {
>                 this.product = product;
>         }
> ......
> }
>
>
> Code Sample for (2)、Root aggregate ID Referenced.
> Product.java
> @PersistenceCapable(identityType=IdentityType.APPLICATION)
> public class Product {
>         // {{ ProductID (property)
>
> @Persistent(primaryKey="true",valueStrategy=IdGeneratorStrategy.NATIVE)
>         private Long productID;
>
>         @MemberOrder(sequence = "1")
>         @Hidden(when=When.UNTIL_PERSISTED)
>         public Long getProductID() {
>                 return productID;
>         }
>
>         public void setProductID(final Long productID) {
>                 this.productID = productID;
>         }
>         // }}
>
>         // {{ Description (property)
>         private String description;
>
>         @MemberOrder(sequence = "1")
>         public String getDescription() {
>                 return description;
>         }
>
>         public void setDescription(final String description) {
>                 this.description = description;
>         }
>         // }}
> }
>
> BacklogItem.java
> @PersistenceCapable(identityType=IdentityType.APPLICATION)
> public class BacklogItem {
>         // {{ BacklogItemID (property)
>
> @Persistent(primaryKey="true",valueStrategy=IdGeneratorStrategy.NATIVE)
>         private Long backlogItemID;
>
>         @MemberOrder(sequence = "1")
>         @Hidden(when=When.UNTIL_PERSISTED)
>         public Long getBacklogItemID() {
>                 return backlogItemID;
>         }
>
>         public void setBacklogItemID(final Long backlogItemID) {
>                 this.backlogItemID = backlogItemID;
>         }
>         // }}
>
>         // {{ Description (property)
>         private String description;
>
>         @MemberOrder(sequence = "2")
>         public String getDescription() {
>                 return description;
>         }
>
>         public void setDescription(final String description) {
>                 this.description = description;
>         }
>         // }}
>
>         // {{ ProductID (property)
>         // this way I means smaller root aggregate,because the current 
> root aggregate do not depends on other root aggregate.
>         private Long productID;
>
>         @MemberOrder(sequence = "3")
>         public Long getProductID() {
>                 return productID;
>         }
>
>         public void setProductID(final Long productID) {
>                 this.productID = productID;
>         }
>         // }}
> .........
> }
>
> Best Regards
>
> Alain
>

Reply via email to