Re: Why can't each Aggregate Root have its own collections of entities/ARs?
Jorge Branco <[email protected]>
| Newsgroups | gmane.comp.programming.domain-driven-design |
|---|---|
| Message-ID | <CAMfCbE_WuXHopmffSoYoc8Y1R8A8m0=+kYQe39mxeUDPm=CWsw@mail.gmail.com> |
Hi all,
I recognize there are several factors at play here. DDD is generally
applicable to large and complex domains. I am well aware that simple
examples, as the depicted on the original post, may look odd because they
are just that -- examples, and that in the real-world, large scaled
application patterns here looking awkwardly convoluted may reveal
themselves quite satisfactory. Yet...
I'll try to answer your comments/questions and I'll throw a couple of ideas
back into the pool, hoping we don't end up with circular arguments in the
way :)
@ Simon
I think you probably need to think more about the behaviour of the system.
> You have described a data model that is a representation of the actual
> world, but does that actually support the behaviour you would like the
> system to display/implement? Once you have created the web of objects as
> described, what do you actually want to do with it?
I was actually wittingly stripping out the most I could as I was a bit
afraid a more complex example would fall in the category of TL;DR. Although
a more mature model would undeniably lead to a more fruitful discussion, it
would also raise the bar to anyone reading it. IMO the example actually
clearly exemplifies the crux of my question -- my lack of understanding of
the advantages of replacing multiple-instance-collections (the "standard")
by singleton-system-repositories (in which all the World instances in the
system are kept/persisted behind a single instance of a WorldRepository).
This is more of a tactical pattern issue than a strategic one. I believe my
described solution to be generally in full agreement with the basic
DDD tenet -- and that's why I find it particularly disconcerting not to
find a single example on the web in accord with it. It makes me think I
must be missing something..
I'll try to illustrate my point again with some more code:
//definition
class World {
private String name;
private IContinentRepository continents;
public World(String name, IContinentRepository continents) {
this.name = name;
this.continents = continents;
}
void addContinent(Continent continent) {
continents.add(continent);
}
...
}
interface IContinentRepository {
void add(Continent continent);
Continent continentNamed(String continentName);
void remove(String continentName);
Set<Continent> allContinents();
int size();
}
//usage
IContinentRepository continentRepository = new
InMemoryContinentRepository();
World world = new World("earth", continentRepository);
world.addContinent(new Continent(...));
This is a basic example, devoid of a lot of details. But the idea is that
instead of having a single ContinentRepository in our system, we'll have
one per World instance. From some preliminary tests, I acknowledge this may
yield a generally more complex repository implementation than the "vanilla"
Repository pattern seen elsewhere.
@Mauro:
using( var universe = repositoryFactory.BigBang() )
> {
> var world = worldFactory.CreateNewWorld();
> var continent = continentsFactory.CreateContinent();
> world.AddContinent( continent.ContinentId );
>
> universe.Store( world );
> universe.Store( continent );
> universe.CommitChanges() ); //here the universe raises events that
> allows the creation of the read model
> }
My "distress" with the example is the major undertaking involved -- 3
factories -- bearing no clear advantage in the process (occam's razor). I
may be missing something, though. Plus, as originally stated, the world in
your example has continents -- their primary id, but to actually make
something with them, for instance, to know how many continents a given
world has, you'll need to *know* some outside class -- a repo, a domain
service.
Getting the number of continents from a World object in my example would
only require a simple
int numberOfContinents = world.numberOfContinents();
implemented as
public int numberOfContinents() {
return continents.size();
}
I'm curious as of how you'd get the number of continents given a world
instance. Would you put that logic in the ContinentRepository? In the
WorldRepository? In a service? In the World class, passing a repository as
argument?
@Tom:
I understand the motivation of Vernon of choosing the disconnected
> implementation, but I whole heartily agree with your statements that the
> natural usage of the domain model is getting impaired.
So it's not only me, lol.
> In my insight Vernon chooses this approach to allow dynamic scaling from
> simple DB to distributed storage like Cassandra, by implementing the model
> already suited for distributed storage.
I was expecting some kind of answer in this line. I assumed that a lot of
what I saw in Vernon's book was done targeting that kind of technologies
(that I do not use at the moment, and that I do not foresee to be using in
a near future) but I was yet waiting for a confirmation.
> Personally I feel not many systems will ever need that level of storage,
> so I'm inclined to keep my model more "sane". The same goes for the amount
> of usage of events; inter-domain, yes. Intra-domain; not if I can avoid it.
That makes a lot of sense. That's my case in point, I believe.
jorge
On Wed, Aug 28, 2013 at 6:44 AM, Tom Eugelink <[email protected]> wrote:
> **
>
>
> I understand the motivation of Vernon of choosing the disconnected
> implementation, but I whole heartily agree with your statements that the
> natural usage of the domain model is getting impaired. In my insight Vernon
> chooses this approach to allow dynamic scaling from simple DB to
> distributed storage like Cassandra, by implementing the model already
> suited for distributed storage. Personally I feel not many systems will
> ever need that level of storage, so I'm inclined to keep my model more
> "sane". The same goes for the amount of usage of events; inter-domain, yes.
> Intra-domain; not if I can avoid it.
>
> Tom
>
>
>
>
> On 2013-08-27 04:57, jorgedfbranco wrote:
>
>
> Consider the following context:
>
> A World(Entity) has Continents. A Continent(Entity) has Countries. A
> Country has Cities(Entity). Let's for the sake of argument assume there are
> invariants involved for each one of the referred entities. As such, they
> are not only entities but ARs as well.
>
> Were I asked in a standard OO course to implement the classes that model
> the shown text, and I'd write the following:
>
> class World {
> String name; //identifies this entity globally
> Set<Continent> continents = new Set<Continent>();
> ...
> }
>
> class Continent {
> String name; //identifies the continent uniquely in a world context
> Set<Country> countries = new Set<Country>();
> ...
> }
>
> etc
>
> **
> **
> Using this code is as simple as
>
> Continent europe = new Continent("Europe");
> europe.addCountry(new Country("Germany"));
> europe.addCountry(new Country("Poland"));
> World world = new World("Earth");
> world.addContinent(europe);
>
>
> Now, from what I've gathered from Eric's and Vernon's book, though, is
> that I should replace this kind of design by one in which instead of having
> each Entity class have its own collections of child entities, to have a
> Repository by Aggregate Root type. I'd then have something of the like of
>
> class World {
> String name; //identifier globally
> Set<ContinentId> continents = new Set<ContinentId>(); //ContinentId
> is a wrapper of a string
> ...
> }
>
> class ContinentRepository {
> ...
> }
>
> etc
>
>
> This IMO raises a couple of issues:
>
> 1. It obscures the code.
> 2. Entities transversion stops being possible without using an outside
> class (that, or each Entity must either have repositories injected or have
> its much needed repositories set as static singletons). That outside class
> may either be a Repository or a Domain Service.
> 3. Something as simple as adding a Country to a Continent will now
> need a Domain Service or a Repository, as Continent doesn't have what it
> takes to do the task at hand.
> 4. Shouldn't we strive for a architecture-neutral domain
> implementation? Just looking at these centralized repositories (instead of
> one collection by each entity object instan ce at runtime) makes me thing
> of a database-oriented application.
>
> When first reading about DDD's repositories, I got the impression they
> would be nothing more than an abstraction of a collection. But from what I
> gather from the examples abounding on the web and on Vernon's book, they
> sure seem more like an abstraction of a *singleton* collection. I call them
> singleton collections because in reality there is only one per type on a
> given system (there's always only a CountryRepository in memory! Only a
> ContinentRepository in memory!).
>
> I'm at the moment quite confused on the matter. It almost seems as all
> the examples I see regarding DDD are database(sql/nosql)-oriented , which
> is acceptable as that is the norm, but some of us work on "purer" grounds
> where no database is involved. What is really the problem with my initial
> bit of code?
>
>
>
>