Reputation: 7915
I'm interested in the Idea of DDD but I have some questions about the concept of encapsulating and protecting the AggregateRoot internal Entities and how to reference them. I created a simple example (don't hit me if the domain design is bad, this is just an example to clarify the question).
// can be referenced from outside, is aggregate root
public class Library : AggregateRoot
{
IList<Shelf> shelfs { get; }
}
// can be referenced from outside, is aggregate root
public class Shelf : AggregateRoot
{
Book GetBookById(Guid Id) {...}
}
// should be accessed through Shelf, not referenced outside the context
public class Book : Entity<Guid>
{
Guid Id { get; } // or something else uniqe, e.g. ISBN
}
// how to reference a book here?
// I think, I should not use Id because this is internal and
//only valid for Shelf Aggregate (but I can't have a second one of this book)
public class Lending : AggregateRoot
{
// feels wrong because of passing internals from Shelf
Guid LendedBook { get; set; }
// should I Clone() an object with unique identity, is this allowed in DDD?
Book LendedBook { get; set;}
// create separate type for lending (but what should this type cointain)
LendedBookInfo LendedBook { get; set;}
}
Hope to get a clear answer, cause most samples are only about ValueObjects (that are easy, 'cause they are copied anyway and not really referenced). I used C# style of code for my sample, but any other programming language or pseudo code is also welcome as answer.
Upvotes: 1
Views: 948
Reputation: 7141
In DDD it's never a bad thing to model the real world. If you spoke to a Domain Expert in this domain, a librarian, they probably have terminology and workflow that could help you model your domain.
For instance they might talk about "lending a book" using a "book ticket system" and when they want to find a book by title they might look in the "title catalogue".
This might lead to a design where you have a BookTicketService
class with a LendBook(lender, book)
method, which might cause an entry to be recorded in the BookTicketSystem
. Or a TitleCatalogueService
class that has a SearchByTitle(title)
method. Or when the library accepts a new book into the library, a record of it probably goes in the AccessionRegister
, etc.
If you model your library using this ubiquitous language it allows other people in the library domain to pick up your software easier and most importantly, it allows software developers to talk to librarians about their requirements without talking in software development terms.
I'd Google a few things like "how to start a library" or "library glossary of terms" and try bring some of the language and processes you find into your software. People already run fully functioning libraries with tens of thousands of books and lenders. In theory all you need to do is understand how they do this, the terms they use and it should be possible to model it in software.
Upvotes: 1
Reputation: 18024
Your example of an external ID usage
Guid LendedBook { get; set; }
is fine. What you could do to make the model more explicit, is to create a value object BookId
and use that one to reference books. The value object BookId
is then a context-wide concept, i.e. not one that is local to a specific aggregate.
Apart from that, you probably don't want public setters or IList
as return type in your model. But I suppose that's just an example and not really the question here.
Upvotes: 1