Return-Path: X-Original-To: apmail-zest-dev-archive@minotaur.apache.org Delivered-To: apmail-zest-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 63A6019A68 for ; Sat, 30 Apr 2016 01:30:12 +0000 (UTC) Received: (qmail 64460 invoked by uid 500); 30 Apr 2016 01:30:12 -0000 Delivered-To: apmail-zest-dev-archive@zest.apache.org Received: (qmail 64424 invoked by uid 500); 30 Apr 2016 01:30:12 -0000 Mailing-List: contact dev-help@zest.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@zest.apache.org Delivered-To: mailing list dev@zest.apache.org Received: (qmail 64410 invoked by uid 99); 30 Apr 2016 01:30:12 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 30 Apr 2016 01:30:12 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 8CEE5C0D25 for ; Sat, 30 Apr 2016 01:30:11 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.179 X-Spam-Level: * X-Spam-Status: No, score=1.179 tagged_above=-999 required=6.31 tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, HTML_MESSAGE=2, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_PASS=-0.001] autolearn=disabled Authentication-Results: spamd4-us-west.apache.org (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.com Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id 8evRuT8KQEnJ for ; Sat, 30 Apr 2016 01:30:09 +0000 (UTC) Received: from mail-oi0-f54.google.com (mail-oi0-f54.google.com [209.85.218.54]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTPS id 21C425F1D4 for ; Sat, 30 Apr 2016 01:30:08 +0000 (UTC) Received: by mail-oi0-f54.google.com with SMTP id k142so137608170oib.1 for ; Fri, 29 Apr 2016 18:30:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:reply-to:in-reply-to:references:from:date:message-id :subject:to; bh=S0p2ISV6iTwGMJT1qClsjFigq4lfMFJGeuhhB7qoMsk=; b=FSvcrd4goAe03erxsUOmEejLpuesCJdJehuFyDt/LGmZ3PAwiAUA7hGXw2nCYy5eHY B9p4gKKDeWMxwohjxvwkS7WVHTiyAoxGVIp5h3FuGBHpowCKtDH6oyQ0U0PB3FLVRaek ohLf+ja0gB4k4hYLV9hxFUmTeHRVnkrhC1mmaUuw/oK2afX2kCWmxgFUvlkX41Y0vF85 CE/B7wtCQoFIKc7hYRFn3o0Kw0nX37JGD2C/bIqzLvMDRSMZ6f04lf1Fy5v5QbGoFQvy 0DJTRYwP98UpUGS04d/ES/CkNIEXySka5osxwbTfci/hEEiZNzR55ZNTL44o02Y3RQ5q DXaA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:reply-to:in-reply-to:references :from:date:message-id:subject:to; bh=S0p2ISV6iTwGMJT1qClsjFigq4lfMFJGeuhhB7qoMsk=; b=HVx9Ym5+nxLRY18Rw6yn1l9eh07tPpCD617HwGlpFCyl4sdK2VNqpG1puqaWXVXr1B 29ECSzh/qnXQSPerLRslf3cIhRTm7xhQMu6h3j8LdXl7T4nT1ayLeHJ7bO0vXps4IZL/ YTiDNt4xwntw46sDuqBfJ52I5oqx75BYUdpWdXeBnyxa56/7cGXnvt1LC+zsnXnhITpG AO6UuTED7Q7Ud/Z2yEx+pYNT9t7zf+F6PO6X3BOz4LjZ5Wg5NJNWZPmpmPmcGau38rJ6 E2Hrr18rbLVOGon/DyvY7nVbEM7KtnbRj6zU95u6qoyal3q6lwU5qnhti2fk3yjfihkw AHrA== X-Gm-Message-State: AOPr4FUYIYRPQy4BZzAyGQLj/OVYbzuzGNugYbVep87LNvgCmoA4VrK1eMHtPbvZBvH7Rr8VpN5Cxzx7DeAFfA== X-Received: by 10.157.4.174 with SMTP id 43mr9958899otm.127.1461979806806; Fri, 29 Apr 2016 18:30:06 -0700 (PDT) MIME-Version: 1.0 Received: by 10.202.225.213 with HTTP; Fri, 29 Apr 2016 18:29:47 -0700 (PDT) Reply-To: niclas@hedhman.org In-Reply-To: References: <5722808E.4080202@gmail.com> From: Niclas Hedhman Date: Sat, 30 Apr 2016 09:29:47 +0800 Message-ID: Subject: Re: Aggregates and AggregateRoots To: dev Content-Type: multipart/alternative; boundary=94eb2c09d9d00dba460531a9b323 --94eb2c09d9d00dba460531a9b323 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable After thinking about this for a while, my conclusion is that on one hand I like it a lot, but it would also mean that we need Aggregation(), ManyAggregation(), NamedAggregation() in parallel of the existing Associations, for something that is really the same-ish thing. It isn't out of the question, but to me it feels like a lot of user-facing surface area for relatively little purpose. Could this be modified a bit?? EntityBuilder builder =3D uowf.newAggregation( myOrder.items() )= ; I think this looks very neat, and should solve the "what is its association" in the UoW part quite easily. WDYAT? Niclas On Fri, Apr 29, 2016 at 7:43 AM, Niclas Hedhman wrote: > > I like this idea. It is very Zest-like, in that it is "think outside the > box"... (There is no spoon) > > I want to ponder over a little bit. > > Cheers > Niclas > On Apr 29, 2016 05:28, "Kent S=C3=B8lvsten" wr= ote: > > Another topic is how to manage the OrderItems inside the Order. > > I have an idea .... > > You discussed a special UOWFactory as part of the aggregate composite - > what should that UOW be used for? I guess it is just for adding/removing > OrderItems. > Could that be part of the relation? > > what if , instead of > > @Aggregated > ManyAssociation items(); > > > we have > > ManyAggregation items(); > > An aggregation is a relation, similar to the association. But we cannot > add items to it, instead it helps us building new parts. > > EntityBuilder template =3D items().newEntityBuilder(); > template.quantity().set(1); > template.newInstance(); // will automatically be part of the collection > > and the later use the aggregation to remove an item. > > That is OrderItems are not created independently and then added, instead > they are created directly on the aggregation! > > /Kent > > > > > > Den 28-04-2016 kl. 17:01 skrev Niclas Hedhman: > > Gang, > > > > since we are on the topic of persistence, I have some thoughts that I > would > > like to share. > > > > I think code is the best way to show, and as usual we start with a > usecase. > > > > So, let's say I want to create the classic Order example with OrderItem > > entities (orders can be massively big doh!!! (should really just use > values > > as items)). Any way, so I have the OrderItem > > > > public interface OrderItem extends EntityComposite > > { > > Property quantity(); > > Association product(); > > Property pricePerItem(); > > Property discount(); > > } > > > > The we have the Order itself... > > > > public interface Order extends EntityComposite > > { > > Customer customer(); > > BigDecimal totalSum(); > > void addItem( Product p, int quantity, BigDecimal price, BigDecimal > > discount ); > > void removeItem( Product p, int quantity ); > > Iterable items(); > > > > interface State > > { > > Association customer(); > > > > Property shippingAddress(); > > > > @Aggregated > > ManyAssociation items(); > > > > Property onCredit(); > > } > > > > } > > > > Let's say that we just want to make sure an order doesn't exceed the > credit > > limit of the customer. > > > > For sake of simplicity in this discussion, the current outstanding cred= it > > is stored in the Customer itself and a simple exposed method for the > > validation. > > > > public interface Customer > > { > > : > > boolean hasCreditFor( BigDecimal additional ); > > : > > } > > > > We already have a validation mechanism called Constraints, but they wor= k > on > > input arguments to methods. We need something similar, but work on the > > entire Composite, > > > > public class CreditLimitConstraint > > implements AggregateConstraint > > { > > public boolean isValid( Order order ) > > { > > if( ! order.onCredit().get() ) > > return true; > > return order.customer().hasCreditFor( order.totalSum() ); > > } > > } > > > > And we could annotate with a Constraint-like annotation, > > > > @AggregateConstraints( CreditLimitConstraint.class ) > > public interface Order extends EntityComposite > > {} > > > > But this doesn't solve the problem. We could have code that queries for > the > > OrderItem instances and manipulates them directly. That is against the > > principle of Aggregates. Or we could hand over a reference of an > OrderItem > > to another composite which use it to direct access to the aggregated > > entity. Also not cool. > > > > So, what we need is that EntityReference of an aggregated Entity to be > > invalid if it is not accessed from the AggregateRoot instance. > > > > How can we do that, in relatively simple terms? > > > > Well, one option could be to create a new composite meta type; > > > > public interface Aggregate extends EntityComposite, UnitOfWorkFactory{} > > > > And its implementation of UnitOfWorkFactory has two purposes; > > 1. Only work for types that exists as @Aggregated Associations within > the > > same subtype. > > 2. Manage the EntityReferences to be Aggregate "relative". > > > > The second of those is a bit tricky, but effectively needs to provide a > UoW > > implementation that leverage the EntityReference of the owning Aggregat= e. > > Doable, but tricky. > > > > > > But hold on a second; If the Aggregate is a UnitOfWorkFactory and > possibly > > handles its own UnitOfWork, how does this fit with DDD on one hand and > with > > the current UoW system on the other hand?? > > > > Well, with DDD it is a perfect match. The Aggregate is a transactional > > boundary. So it seems to suggest a good fit. Cool. > > But, does DDD really tackle this, since you can't get the > UnitOfWorkFactory > > of the Aggregate without first having a UnitOfWork to retrieve it with. > > This seems to suggest that DDD doesn't have it correct, or has made a > > simplification that is somewhat unfortunate. > > > > Could it be that there is actually a difference between the Aggregate > being > > a non-Entity composite and the AggregateRoot that is an entity and part > of > > the Aggregate?? > > > > Perhaps... > > > > public interface Order extends EntityComposite {} // back to normal > entity > > > > @AggregateConstraint( CreditLimitConstraint.class ) > > public interface OrderAggregate extends Aggregate {} > > > > meaning > > > > public interface Aggregate extends > > UnitOfWorkFactory, Composite > > { > > T root(); > > } > > > > (I have place EntityComposite supertypes everywhere, but that is for > > clarity. Since we don't require that anymore, they shouldn't be there) > > > > So, then how would this be used?? > > > > Aggregate aggregate =3D module.newAggregate( Order.class, identi= ty > ); > > > > the newAggregate method, would do > > a. create Aggregate composite, > > b. create a new unit of work > > c. read the entity from the store and populate root() > > d. if entity doesn't exists, create a entity builder internally and > use > > when populating state, > > e. keep the UoW open, > > > > Since it should handle both existing and new aggregates uniformly, I > think > > a > > > > boolean isCreating(); // or similar > > > > method should also exist on the Aggregate type. > > > > > > To me, this feels a lot better. The DDD "rules" and "intents" are still > in > > effect and can be enforced, and we can manage to implement it in Zest, = if > > we choose to do so. > > > > I am really keen on hearing thoughts on this topic. > > > > Cheers > > --=20 Niclas Hedhman, Software Developer http://zest.apache.org - New Energy for Java --94eb2c09d9d00dba460531a9b323--