Wednesday, 21 September 2011

Enterprise Software Architecture: How To Do It Part 3

This is part 3 in a series. For earlier and later posts in this series, please see here: Enterprise Software Architecture: How To Do It. For the accompanying code, click here.

Coding the Domain

Rather than endlessly explaining how and why things should be done with the odd code snippet, I'll jump straight in and supply some code and the explain what I did and why I did it:

Click here to download Client Data System at CodePlex. It's the first change set: 3875.

If you open the solution and expand the Lucid.ESA.ClientDataSystem.Domain project, and then expand the entities folder, you should be able to recognise the entity classes, all the properties they hold, and all their behaviours.

You may have some questions...

What are all these things I don't recognise?

Firstly, some things that you should not worry about for now: Ignore Common/IRepository.cs and everything in the RepositoryContact folder. Ignore the fact that all the entities inherit from a base Entity class, except for the fact that the Id property (which you may have noticed was missing from the entities) is in here. We'll come to all these later.

Why do the entities all have a static Create method? Why can't the creation of the object be done in the constructor?

Eric Evans and the DDD community refer to this as a Factory, which is based on the Gang of Four's Factory Method pattern. There can be many factory methods for an entity. This way, there can be different ways of creating an entity depending on which one is called. If this was done with constructors, each constructor would need a different signature. Also, a factory method does not have to return the base type of the entity. It could return a sub type, and which sub type is returned could be decided by business logic in the factory making decisions based on the information passed through in the parameters. An alternative to the Factory Method is an a dedicated Factory Class or an Abstract Factory.

Why bother having these complex parameter classes that are passed in to the Create and Edit methods? Why not just set the properties of the entity?

Remember this is a simple example. In this particular case, the majority of the functionality of the Create and Edit methods is to set the member attributes based on the properties of the parameter objects (one exception is that _deleted is defaulted to false). In most cases, more complex business decisions would be made in here based on what was passed in through the parameters. For example, the Site entity may have an _overseas attribute, which would have to be calculated based on the address passed in in the parameters, and as mentioned before, it may want to return a sub type rather than the base type, depending on what parameters are passed in. All this business logic has to be done somewhere, and that somewhere should be in the domain. If the properties of the entity were set from the outside, it is possible (and usually likely) that the business logic would get pushed out of the domain and into the service layer, or worse, the UI.

Running the Tests

All the logic in the domain should be tested in the form of unit tests. In this case, these are in Lucid.ESA.ClientDataSystem.UnitTests. I have to say, I have not followed any strict testing methodologies here. I have yet to read Roy Osherove's The Art of Unit Testing but it is on my to read list, and anyone who wants to know how to unit test properly should read this. However, these tests demonstrate how to use the domain: build a 'create' parameter object, call the Create method to return an entity, and call Validate on the entity to ensure it is valid (if it is, no records in the returned collection will be present). Then you can build an 'edit' parameter object, pass that into the Edit method of the entity and Validate it again.

Next post: Persisting the Entities.

No comments:

Post a Comment