Fri Nov 7, 2003 7:22 pm
I'm new to this list, I enjoyed your book Eric and did not put it
down until I had read it from cover to cover. I went searching to
see if I could find any more books you may have written and found
this list. I was somewhat surprised to see how actively you
participate in the list and it seems like such a privlage to have
the opportunity to interact with you this way.
One thing in particular that caught my attention in the book was the
part about aggregate boundries.
I like to abstract things and break things down into smaller and
smaller reusable components? supposedly anyway, but most often I've
found I endup with a proliferation of classes and have difficulty
enforcing the rules of the smaller components without the context of
the object its contained in. I began to realize that to enforce the
rules I needed to keep these objects internal to the aggregate and
expose appropriate functions as necessary taking some kind of index
or handle to the element it desired to get/set or otherwise modify.
However then I would end up with numerous methods in the aggregate
to modify its contents that seemed like perhaps they belong in a
separate object, if only to make the public interface easier to
understand. So I would create a speciallized class that is tightly
coupled to the aggregate root that would be returned in place of a
handle to the object but would internally hold a handle and if was
asked to modify itself would actually forward the call to an
internal function of the aggregate root along with the handle. This
was no different then having all the functions in the aggregate root
but did allow me to expose a simpler public interface and keep all
the rules in the aggregate root. After reading your book I began to
recognize the value of immutable value types. If I understand this
correctly an aggregate should not expose an internal entity to
external modifications, rather one should immutable classes, or
structs that implicitly copy of themselves (or even callling
implementing a clone method on a mutable class) that would allow a
simple and rich interface to create the contained objects then a
method on the aggregate to replace the entire object and check the
rules; if modification is required a copy would be returned to the
client which can modify it as desired and tell the aggregate to
replace the entire object allowing the validations to take place
again. With data objects I guess I've always done this, for example
a customer that contains an address. Rather then the customer
providing set street, set zip, could provide set address.
However, I'm still having difficulty applying this pattern when the
aggregate contains objects with internal entities like the car
example mentioned in the book where the tires are enties within the
aggregate, lets say the user wants to do more with the tires then
just rotate them or replace them. Lets say perhaps Slash() them
after a tire has been slashed one can no longer Drive() so the car
needs to be notified of this. As I said above one of the ways I've
found to enforce this is by placing the method into the aggragate so
what I would have is car.SlashTire(position) and likewise with any
other function of tire would need to be reimplemented in car, as the
number of these internal componenents increases car becomes littered
with functions related to tires, and everything else so what I would
previously do is make SlashTire(posiotion) an "internal" method and
implement a CarTire class that would call Car.SlashTire(position)
rather then cluttering the public interface of car, but that just
made it look pretty on the outside but the inside was a mess.
Perhaps a better implementation could be a TireInfo value object
that could be used something like Car.SetTireInfo( position,
Car.GetTireInfo(position).Slash() ) allowing the car to be notified
of the change and presenting a rich set of TireInfo fuctionallity.
It could be a bit misleading however, as calling Car.GetTireInfo
(position).Slash() would accomplish nothing, but I believe it is a
better solution. The .NET string class might present the same
confusion (calling mystring.Trim() expecting mystring to now contain
a trimmed value rather then using the result) but to have
implemented it otherwise would cause far problems then confusion.
I have been a bit confused about the statement in your book (last
sentence of p128) "?The root entity can hand references to the
internal entities to other objects but those objects can use them
only transiently,?" is this only applicable when that object is
immutable or the aggragate doesn't care about the modifications to
that object?
The immutable value object approach though seems a bit lacking
however when aggregates contain other aggregates or collections
containing collections that affect the aggregate. For example take
the ListView class for example that contains Items containing
SubItems. If a subitem is modified the aggregate must be notified
so that it can repaint itself. I see they chose not to implement
Items as a value object but return a referance to a
ListViewItemCollection object that must apperantly notify the
ListView when changes are made. ListView item appears it could be a
value object however I found listView1.Items[0] returns a referance
allowing me to modify the list items directly such as listView1.Items
[0].Text = "newText". If it was a value object the item however it
would still display "text" after executing listView1.Items[0].Text
= "newText" so its definatly a referance to the internal object. I
am a little curious about the internal implementation of listview to
see if they used an approach like I did before where ListItem would
call some internal method in ListView to tell it that it has been
changed (probably so as the public properties includes a ListView
referance).
Although I have noticed another relevant pattern for maintaining
integraty of an aggregate used quite frequently in UI, the
observer/events. Perhaps list view does it this way. Form contains
controls which can contain other controls, etc.. although the button
control may not be a accessible via a public method of the class,
the button is expossed directly to the user for him to Click() so in
a someways it is almost like exposing the button in the public
interface. When that button is clicked the form needs to
acknowledge the change in state and does so by subscribing to an
event. I was thinking this same pattern could be used even above
with Tires, when it is Slash ()'d an event could be raised notifying
the Car it can no longer Drive(). However if this approach is used
one must be careful to ensure the same tire is not added to more
then one car. Coneing it when added was my first thought but then
if the user were to do Tire tire = new Tire(); Car.Add(tire);
tire.Slash() the cloned object would not be affected and would be
confusing. This made me kinda curious about listView so I did a
test to see if it cloned it once it was added and it was not, so
then I tried adding the same item to a second list view and it threw
an exception stating it must be removed from the previous listview
before adding it to a new one. ListItem contains a referance to
ListView so apperantly one rule for Add is that ListView == null.
Not sure where I'm going here but anyways I thought this was
interesting now that I'm more aware of aggregate boundries some
other implementations that have been used. It seems like every
aggregate could possibly become an local entity with in another
aggregate to build something larger and the larger (like forms
containing controls containing more controls) so perhaps it is not
always necessary or practical to always return a immutable object.
And one of the other methods could be used. I'm still trying to
diggest all of this I guess, and I'm curious what thoughts people
might have on these methods and when it is appropriate to use
immutable objects, an speciallized referance to call an internal
aggragate function, or mutable object that raises notification
events? Also currious if anyone has noticed any other patterns?
KurtHarriger
The essence of aggregates is to identify a cluster of objects that
are interrelated and can be conceptually thought of as one unit. You
identify rules that apply to the aggregate as a whole and make sure
they are enforced. As with any pattern, there are a lot of options
as to how you actually do this. You might prohibit the modification
of the internals of an aggregate except through the root, as some
have suggested here. This would guarantee the aggregate's integrity,
but the interface of the root might get bloated. Sometimes it is
more natural to allow some specific internals to be manipulated
directly.
Let's suppose you had a nice framework that let you explicitly
declare your aggregates and could trace to the root from any object.
At commit time, such a framework could identify any "dirty" (i.e.
modified) objects, find their roots, and invoke a validation method
for the whole aggregate.
Or, you could have an event mechanism, where a setter method
triggered a notification which the root, or perhaps some particular
rule, might be listening to, resulting in a validation.
Without the framework, you are forced to embed knowledge of
aggregate management in the application. For example, you could
invoke the root's validation method(s) each time you modify an
element. You do always know the root because you must have traversed
to the inner element from there. (That is one of the aggregate
rules.) This forces the app code to carry around the context (the
root) as it works with inner elements, and be aware that there are
invariants that have to be validated. But you could use a coding
convention, or a very light framework, to standardize this and it
could be workable.
The point is that these all follow the aggregate pattern.
I'll try to add a concrete example of what I mean later this evening.
EricEvans
It's this
Prevayler's neighborhood? The prevalent system is the root of
the object graph and all modification occurs by executing commands on
the prevalent system. I suppose you could consider a large command set a
"bloated interface," but I have found commands to be a good way to
manage this complexity.
The prevalent system is then just a hierarchy of classes and each
command operates on the smallest part of the hierarchy needed -- that
is, as close to the leaf nodes as it can get. This ensures a small
"blast radius" for changes, minimizing the overhead of validating the
object graph as we go.
As for "Sometimes it is more natural..." I have found that other forces
push me towards the command-based design, so it is /not/ natural for me
to allow that. That's just one man's opinion.
Still, I'd love to know more. I am just a guppy in this pond.
JBRainsberger
I don't think that object prevalence methods (as implemented in
Prevayler) lead you "naturally" to the "Aggregate Boundaries" pattern
that Eric Evans is talking about. It's true that all access to
persistent objects is through the "root" system object. But there's
no object prevalence rule that I know of that would encourage people
to organize all the Entity instances in a strict hierarchy. It seems
more likely, in most systems, that the instances would end up with a
spaghetti web of relationships for convenience and performance.
So Eric Evans' "Aggregate Boundaries" pattern would probably still
prove useful in an object-prevalence-based application.
JeffGrigg
Often times I need an immutable collection within an aggregate.
Each item in the collection has an internal identity (such as a
name). Once the collection is created it will be used in an
aggregate and should be immutable. I have a few ideas but I'd like
like to get some input on the pros and cons, the preferred method or
never do that kinda input.
For example:
A specification object contains information about the fields and can
calculate an offset
The first question I have is specifications a collection or an
aggregate for example:
// a collection
class Specification : ICollection, IEnumarable, IClonable
{
Specification(FieldSpec[]);
FieldSpec this[string name] { get; }
int GetOffset(string name);
}
// an aggregate containing a collection
class Specification
{
Specifications(FieldSpecCollection specs);
// desires FieldSpecCollection to be immutable
FieldSpecCollection Fields;
int GetOffset(string name);
}
|
I'm leaning towards the aggregate approach. I really not sure what
the pros and cons are I just like the aggregate implementation
better I guess the pro is that it delegates collection management to
another object, need to create another class.
So moving on the collection contains FieldSpecs:
class FieldSpec
{
PSType type;
int Size;
}
class FieldSpecCollection : ICollection, IEnumerable, IClonable
{
FieldSpec this[string name] { return (FieldSpec) base.BaseGet
(name); }
//...ICollection, IEnumerable, IClonable implementations
}
|
Now if I add an `Add' method to FieldSpecColleciton it is no longer
immutable. Here are some options I can think of so here are some
options:
Method 1: Specify all data at creation
FieldSpec(string[] names, FieldSpec[] values);
This would work, however preparing the data to be added to the
collection would make errors more likely. each collection would
probalby have 80+ specs that makes new string[] {...}, new FieldSpec
[] {...} very messy and error prone. Even if another method is
better I don't see much of a reason not to provide this constructor
just not convienent.
Method 2: Make the collection a list. Give the internal identity to
to the item. I could create another class that provides the
association between name and value ie:
class FieldSpec
{
PSType type;
int Size;
string name;
}
|
Now the constructor can take an one array rather then 2 arrays read
through it check all names are unique.
I really don't like this idea what purpose does FieldSpec have
knowing its name identity now its not a value type, but its only
unique in the context of its container so its not an entity either.
Method2b: Make collection a list. Create an association aggregate
Okay well we could minimize the impact by moving it to another class
specifically for its container.
class FieldSpecCollectionItem
{
string name;
FieldSpec spec;
}
|
Still don't like it same reasons as above but at least FieldSpec
remains a value-type.
Method 3: Attribute enforced immutablility, Readonly property
I really want an add method but if I define one its not immutable, I
could perhaps change the state of the object from mutable to
immutable with an attribute like readonly.
Fields.Add("field1", FieldSpec.PSDate);
Fields.SetReadonly();
return Fields;
I don't really like this method either as now I have to be careful
to ensure any method of property that would modify the object throw
an exception what if I forget. Its also rather confusing to the
Specification user to see add methods they can't do anything with.
Method 3: Builder
I could create an entirly seperate class that would be mutable
provide add methods, remove, or anything else I desire and then a
method to create a readonly object
class FieldSpecsBuilder
{
Add(...)
FieldSpecs GetFieldSpecCollection ();
}
|
This could work perhaps after gathering the information it could use
the constructor to create a new instance of FieldSpecCollection.
This is better but now I need another class that will be affected by
any changes to FieldSpecs and I have to duplicate all the rules
(although this is not critical as long as FieldSpec constructor
checks them, but it would be much nicer to have it throw an
exception when you add a duplicate field rather then when it is
converted to specs).
Method 4: Separate interface
Hmm that gave me an idea what if the Specifications class does not
contain the collection by name but via a readonly interface.
interface IFieldSpecCollection : ICollection, ICloneable, IEnumerable
{
FieldSpec this[string name] { get; }
}
|
Now I can implement FieldSpecCollection like the builder but have
only one class and one set of rules:
class FieldSpecCollection : IFieldSpecCollection
{
void Add(string name, FieldSpec spec);
IFieldSpecCollection GetFieldSpecCollection() { return
(IFieldSpecCollection)this; }
}
|
I like this much better. However nothing prevents the client from
downcasting to the actual object and modifying it. Perhaps if the
client application does not need to see FieldSpecCollection I can
mark it as internal then they couldn't downcast.
Method 5: Combine separate interface and readonly property on clone
For the truly paranoid we could probaby create a readonly interface
and a method GetReadOnlyInterface() that would create a copy, and
mark it as read-only.
Method ??: Anyone think of anything else?
Okay lets complicate things more in the not to distant future I
could see adding formatinfo. Each field can optionally have
formatting rules, legacy system often requires fields to be right
aligned or it won't find the record. This is very anoying and
difficult to troubleshoot? So now I need to put this somewhere? the
question is where?
class Specification
{
FieldSpecCollection FieldSpecs;
FormattingInfoColection FormatInfo;
int GetOffset(string name);
}
|
Now the specification class seems perhaps a logical place. We must
enforce that you cannot add format rules if there is not FieldSpec
for that field. I guess the constructor can do this but I want to
simplify the creation of this object I'm going to be adding many
items. Perhaps we could just add it to fieldspec.
class FieldSpec
{
PSType pstype;
int size;
FormatInfo formatInfo;
}
|
I don't like this now instead of a few static instances of
FieldSpec (excluding string that must be created with a specified
length) we end up with numerous instances.. Doesn't seem to belong
here. Perhaps add it to FieldSpecCollection so that I can assign it
when I add the other items.
FieldSpecCollection
{
Add(string name, FieldSpec spec, FormatInfo format);
// hmm this would be ambiguous maybe remove it and use other
properties
//FieldSpec this[string name] { get; } // hmm what am
FieldSpec GetSpec(stirng name);
FormatInof GetFormatInfo(string name);
}
|
Really doesn't seem to belong here still think it goes in
specification.
I just realized that allthough I didn't state it above I assumed I
would be creating the specifications something like:
Specifications(specBuilder.GetFieldSpecCollection());
However on second thought? if Specifications had a mutable object to
work we might have something like this:
class Specification
{
Specification() { _specs = new FieldSpecCollection(); _formatinfo
= new FormatSpecInfo(); }
Specification.Add("name", spec, format)
{
FieldSpecs.Add("name", spec);
FormatInfo.Add("name", formatinfo);
}
IFieldSpecCollection FieldSpecs { get { return _specs-
>GetReadonlyCopy(); }
}
|
I like that but now we have that problem where specs is not an
immutable object anymore? Again a couple options:
1: repeat above:
I guess I can just repeat what I did with FieldSpecCollection and
provide a readonly interface
2: mark internal provide abstract base
but maybe its not necessary as once a DataStructure is created with
this specification it doesn't need to be accessible from
datastructure except by the marshaller that will be defined in the
same assembly. So I could mark the method as internal in place of
public. But there is always a catch if I want to define custom
marshaller they cant access it so instead of implementing an
interface they could derive from a abstract base I guess.
I didn't realize I had so many options when I started... sometimes
writing this stuff down helps flush out some ideas... I'm still very
interested on opinions? If anyone has actually read all the way to
the end, lol!
KurtHarriger
I was reflecting on the message I wrote before and it finally
occured to me that I was not properly encapsulating the collection.
The collection is really an implementation detail of the aggregate.
I was trying for some form of delegation I thought to perhaps make
the aggregate less complex, but the result violated encapsulation
thus my problem.
However in the many frameworks I frequently see aggregates that
return referances subobjects allowing you to modify them dirrectly
(although mostly just designed to appear that way), such as the
listview control. It occured to me to use ildasm to view the
implentation details of the listview control, and found the listview
had numerous private methods called by the nested collection classes
to insert columns and listitems, the collection interface it exposed
was mearly a front for users to group the functionality and make it
easier to understand. So rather then reducing complexity of the
aggregate as I was thinking it actually increases the internal
complexity of the aggregate.
I must have missed the point of aggregate boundries focusing on the
immutablility apspect thus eliminating the need for backreferences
from contained objects. Although returned values no longer need
backreferances for things like collections and internal aggregates
cloning can be inefficient or not-supported... and thus the purpose
of encapsulation. To protect the internal data from unknown
changes.
The point was perhaps not immutablility but that any modifiable
object returned by the aggregate is not encapsulated thus would
be "outside the aggregate boundry." Reflecting on the .Net listview
control listitem would be said to be outside the aggregate boundry
although it is contained within it. The aggregate no longer has
control over the contained object's values and has 3 options:
1) assume the object will always have a valid value, might be
applicable if you know the object returned will not allow the user
to specify a value you can't use.
2) check the value before using it within the aggregate ie:
car.drive() would check cartire.isflat() and throw an exception.
3) require notification of changes via some programming displine.
This could be the users responsibility but would generally be a bad
idea as they are likely to forget at somepoint (like allocating
memory in C). Thus a common enhancement to this method is to return
an object containing a backreferance to the aggregate (so much for
that containment rule) that would enusre the aggegate is notified of
changes to the object. The primary motivator for this option would
mostlikely be when the ability to subclass the contained object is
desired as the contained object cannot be made immutable. Such as
the .net listitem class, otherwise I would think method 2 is
prefered.
One OO principle I've tried to follow is that "a contained object
should not know anything about its container". Frequently with
aggregates I find I must break this principle. I thought I was
choosing poor designs but now I realize that this guidline should be
broken if the objects sole purpose is for use within a specific
aggregate that msut recieve notificaiton of changes.
So it seems to me that when implementing aggregates bloated
intefaces can be neccissary evil, immutable return values should be
strived for if possible and any modifiable object returned if does
not fall into case 1 or 2 should have a backreferance to the
container breaking the containment principle.
KurtHarriger
I largely agree with what you've said.
I think I have overemphasized the case of internal elements of the
aggregate which can be passed out and modified. Most internal
elements will be encapsulated, which means it is much easier to
regulate modifications via the root.
Also, you are right that ad hoc approaches to maintaining aggregates
can push you to put backpointers that you would rather not have. It
is much nicer to have a framework that, once an aggregate is
declared, can do the necessary tracing from an object to its root for
purposes of triggering validations or whatever.
The concept of aggregate is fundamental: that there are groups of
objects that conceptually constitute a single whole, that rules apply
to those aggregates as a whole, and that the design should recognise
them, and not treat every object as an autonomous element.
Beyond that, there are a multitude of variations, and technical
challenges. Actually, the discussion here is forcing me to think
through aggregates more deeply than before. Eventually, maybe I'll
come up with some useful new insight.
EricEvans
> The essence of aggregates is to identify a cluster of objects that
> are interrelated and can be conceptually thought of as one unit. You
> identify rules that apply to the aggregate as a whole and make sure
> they are inforced.
Here are two stories on aggregate boundaries and persistence. They
both have the same two lessons from two different domains. This is off
the top of my head from experiences quite a while ago. I'd like these
stories to become useful in their domain-driven aspect as well as in
their technical description, but I don't want to get too technology
specific up front.
The first domain is shipping. There are packages to be shipped,
packages en route, and packages that have been shipped. There are
shipments to plan (schedule, route, fit into vehicles, etc.) There are
fleets with locations, schedules, capacities, etc. There are customers
with purchasing power, payment options, etc. There is a lot of history
in each of these areas that can inform the business.
The second domain is printed circuit board manufacturing. There are
products to be scheduled, products being made, and products already
out the door. There are orders to plan. There are manufacturing floors
with machines laid out. Machines have locations, capacities,
configurations, set-up times and maintenance needs, etc. There are
customers with purchasing power, payment options, etc. There is a lot
of history in each of these areas that can inform the business.
Each of these areas form domain-based aggregate boundaries. Pieces of
information from each form multiple instances of planned, current, and
historical state transitions. Having all this information in one
runtime model is known to choke the system and not meet specific
needs. Identifying interaction points, separating planned/current from
history, and segregating/replicating the planned/current information is
known to facilitate multiple designs that are tailored to specific
needs.
More later... feedback please?
PatrickLogan
AggregateBoundries is mentioned on: ThreadView