Domain Driven Design API Question
I'm new to DDD and I am working on my first project, which is for an online golf ou开发者_如何学JAVAting registration process. my requirements are pretty simple. users register for the outing and can optionally add a foursome. they can also sponsor a hole with a message and a few other things, but i want to hash our the foursome stuff first.
so, my first though my aggregate contains the a registration entity, foursome value object (which contains a team name and 4 player value objects).
when designing the api, i'm thinking the following pseudo code:
Registration reg = new Registration();
Foursome foursome = reg.CreateFoursome("My Team");
foursome.Player1.Assign("John Doe", 5, ShirtSize.XL);
reg.Register();
My question is, one of the internal components of the aggregate is being exposed to the client code, so am I opening my self up for issues? any flaws with this simple design or alternative apis?
any help would be great as i am in a state of analysis paralysis right now!
thanks
Lets start with Aggregate definition:
A cluster of associated objects that are treated as a unit for the purpose of data changes. External references are restricted to one member of the Aggregate, designated as the root. A set of consistency rules applies within the Aggregate's boundaries.
Aggregate is a group of objects that you would not want multiple users to edit at the same time because it can break domain invariants. Aggregate is also a life cycle unit. It is hard to answer your question without knowing what these invariants, consistency and life cycle rules are. Would creating two Foursomes on the same Registration be bad? Would Foursome with unassigned Player1 be invalid/inconsistent? Would not calling Register on Registration object will 'corrupt' it? If one of the the answers is true then you should not expose your objects like that. This code should be hidden inside your aggregate.
It also looks like Foursome is not a Value Object because it is mutable. It maybe an entity that should be protected by Registration aggregate root.
// PlayerInfo is a value object
public static Registration CreateNew(String foursomeName, PlayerInfo player1, ...) {
if (foursomeName == null) {
throw new ArgumentNullException("foursomeName");
}
if (player1 == null) {
throw new ArgumentNullException("player1");
}
Registration reg = new Registration();
Foursome foursome = reg.CreateFoursome("My Team");
foursome.Player1.Assign(player1);
if(player2 != null) {
foursome.Player2.Assign(player2);
}
reg.Register();
// return consistent and valid Registration instance
return reg;
}
Again, this can be not what you want, it really depends on your domain model. Maybe your Aggregate root should be an entity like FoursomeRegistartion. Maybe Players are aggregates themselves if they can exist outside Foursome/Registration boundary. As others have said, it is hard to get model right the first time. Have a first implementation and refactor continuously.
For a first shot it isn't bad. It is important when starting design not to get too hung up on the small details. How much time you spend worrying about the robustness of the design is going to be primarily a factor of the project's importance. You will continually learn as you build, and over time, your old code will require refactoring to enable new details.
Take for instance, your FourSome class I assume has four players. Could you instead have a Team class which has a Size constraint, such that a Players collection could be constrained to that number of players? Should there be a Player class (or perhaps that is the type of Player1?)?
The answers to these questions will have implications toward the extensibility of your system.
I encourage you to write each of your scenarios into tests (you could use user stories or use cases too, but developers like to write code), and as you write the tests, fill in the Registration, Player, and Foursome/Team class implementation to make the tests succeed. As you expand your system, your tests will change, and so will your design.
Post Addition 1:
Domain driven design is intended to be an approach to developing the classes and data structures your application will use such that it "models" the problem domain. In your case, you are working with a Golf Outing Registration system. Therefore, as you think about the entities which might make up such a system, you might describe how a Team Captain registers his/her Team (including other Players) by providing his Registration. The registration might be for an Event, which itself may have details such as Organizer, Sponsor, etc. As you see, each of the "capitalized" names become your entities (classes). At least for the first draft of your design. As you discover more information about the relationships between your classes, especially in how they interact (a Player is added to a team), you will flesh out the methods of your class.
During this process you can inadvertently introduce design flaws. For instance, a FourSome is, technically, a type of Team limited to four players. Does it make sense to derive a class from Team and set a limit of four players, or does it make sense to put a constraint on Team? That is a design decision determined by... your business rules and you. Is it a mistake to take one approach over the other? Time will tell, as you will need to constantly refactor in order to expand the system.
You will find many patterns during your design process that may make your design process easier, but a new designer typically does not have the experience to recognize when to use them. If you do, kudos to you. You will not find a perfect design the very first time you are designing. I still look back (15 years now) at designs I thought were awesome, and shake my head at my youthful exuberance.
You should describe your problem (to yourself or on paper). Underline the nouns. They are your candidate classes. Tell a story for each user interaction with the system, and the verbs should get you started toward what the methods are on each class. Avoid using the Registration to do all the work, but separate the responsibility of the classes to where it is appropriate. For instance, you can add a player to a team, or you can have a player add itself to a team. Determining where that responsibility lies should be guided (but not dictated) by the SRP, among other design guidance.
Finally, there is no right answer in design. I could tell you based on my extensive design experience but limited golf experience what I THINK it should be, but ultimately your best design depends on the features, scope, and design goals (like extensibility). Start with your best shot, write tests against it, and your design flaws will emerge before you know it. :D
Post-Revision 2:
I think you are reading too much into Eric Evan's guidance. I don't believe he is saying that you can't expose Player from Foursome.
精彩评论