Carpool Project: Part #2
October 13, 2010 Leave a comment
The next section in DDD is about factories – where you create classes that control the creation and updating of these . These classes are not part of the domain model but are part of the domain design. That would be easy enough to do right now and I have taken that approach on several projects, but I want to take a different approach using POCOs and ViewModels. The problem with DDD is that only have 1 representation of an entity/value object. In most applications, there are different flavors of a given value object and depending on the circumstances, different behaviors from the objects.
I then thought of soft drinks. You have a base class – Coke – for example. From that, you have Cherry Coke, Vanilla Coke, etc… Each one of these specialty flavors start with the base Coke and then add something new to it. In addition, some other flavors of Coke (Diet Coke) actually DON’T start with the base Coke. This distinction points directly to Evan’s notion of ubiquitous language.
Going back to the Coke example, you can either use 1 factory that can spit out any flavor of Coke (G0f4 Factory/Builder pattern) or 2 Factories. Factory #1 takes in raw materials and spits out base Coke. This base code can then be used directly by the Consumer(UI). The base Coke can then also be used by Factory #2 that takes in base Coke and some flavoring and spits out Cherry Coke. From a design point of view – does it make sense to have a factory for each variety of Coke? In addition, what happens when a new flavor is invented? Is it better to make a new factory or just add a new loading/unloading bay to an existing factory?
I suspect, like most things, that the answer is not clear-cut. Given the choice of 1 uber-class with lots of methods (or 1 method with multiple inputs via dependency injection) or many classes (like a plant – many factories in 1 place), I lean toward the later. Creating classes are cheap and as long as they use an interface (or a base abstract class) you can enforce some rigor in its construction. In addition, if you have some business logic around Cherry Coke, the code has to written somewhere. Using DI, many classes, or any other technique does not magically remove the need to write fewer lines of code (assuming all are following DRY principles). So if the code needs to be written somewhere – where is it better to be placed? My answer follows Martin and Fowler – more classes with functions that do only 1 thing. Organizing projects via namespaces and VS2010 is a snap and the benefit of having clear-cut responsibilities is clear.
Side note, the problem is that if you have a Brownfield app that many developers have touched over the years. Some might believe in 1 factory using DI, others might believe in 1 factory using extension methods, still others might believe in a plant concept. What happens when all do what they want on the project? You get a mess. That is why having readable code, ubiquitous language, and a standard domain design is so important.
I then launched into DDD’s section on specification – specifically predicates. An example shows better than a textual explanation. Is it better to have a Trip.IsInvalid() method with a collection of invalid reasons, a Trip.HasNoDiver() method + Trip.DoesNotCoverPractice() method, etc.., or an InvalidTrip Class() with a property that gives the collection of invalid, or an TripHasNoDriver() class? I can see the merits of all 4 possible solutions – I generally use the 1st option – but just because I used it before does not mean it is the right way. DDD recommends the 4th option – having a class for every violation of business rules. Depending on the number of business rules, this solution can get pretty unwieldy. Since I am doing this exercise as a chance to learn, I will try option 4 and see where it gets me…
I wanted to start building factories but I wanted to do it in context of the POCOs that the Presentation Layer will consume. I am using a ViewModel pattern so I started listing POCOs that tie directly to the User Stories. Here is my strategy:
· Build a View Model (using an interface or an abstract class)
· Build a Factory – input is the Domain Model, output is the View Model
· Wire it up
My 1st stop is the Car Pool Class (renamed from Trip to have ubiquitous language). Taking the Domain Model’s Car Pool, I stubbed out what the 1st Car Pool View Model would look like:
Notice that the ViewModel Car Pool takes all references and makes them the description of the reference – effectively taking classes and making the strings. My first question – what do I call the Car Pool View Model? Since there can be an infinite number of CarPool-based View Models, I need something that is descriptive enough but still allow for additional View Models – CarPool1 won’t cut it. My typical naming is to call it CarPoolSummary but that locks me in because what if I need two different types of summaries? I then thought that if the summary class covers all of the Car Pool class, I can always subtract fields in the View. I then thought – but if I do that, I am getting away from 1 ViewModel per View. I then decided to tie my class to the actual view that it is supporting – the CarPoolSummary ViewModel will tie to the Summary View. Not great, but I will work though the permutations to see how well this holds up.
My model now looks like:
I then went to add another View Model based on the next Use Case when I realized I really need a description field for each car pool. This string needs to be a combination of Dates/Times/Teams/SwimmerGroups – something to show the user. I then added the next View Model like so:
Calling two points a trend, I then finished the rest of the view models. Note that I made the following changes:
· The car pool description changes depending on if the driver has been assigned, the swim team/groups, and the time leaving.
· Most of the time, there is only 1 swim group per practice. Occasionally, practices are combined with groups (but never teams).
· I removed calculated fields from the Domain model -> practice start and end times are in the domain model, practice length is in the view model
Here is the resulting View Model:
I think this enough to go with, so I then went to write my POCOs.