Announcement Announcement Module
Collapse
No announcement yet.
Domain Models and Validation – An Architectural Discussion Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Domain Models and Validation – An Architectural Discussion

    Warning: this is a very long post

    I would like to start a discussion about the business tier, including the business domain models and validation rules (business rules). This discussion is not really specific to rich client applications, however, because a rich client application is by definition a remote application that requires domain information to be serialized and sent to/from a remote service, many of the tricky problems associated with the business tier are more apparent.

    As a starting point for the discussion (I hope others will find it interesting and jump in), I’ll highlight how we think this should work, and the problems we are encountering in real life situations. Of course, please jump in if you see statements or ideas that you disagree with, or have alternative solutions or suggestions.

    First though, a few definitions:

    Business Tier:
    The business tier represents the inherent business problem that a system is solving. Note that it is not tied to a specific presentation technology. It should be possible for both technical people and business people to review models of the business tier and ‘see’ the business entities, relationships, core operations, and business rules.

    Presentation Tier:
    The presentation tier represents ‘what the application looks like’. It should be possible to have multiple different presentation tiers that use the exact same set of objects from the business tier. For example, you should be able to easily replace or run in parallel:
    - a web interface based on Struts
    - a web interface based on Spring MVC
    - a rich client interface based on raw Swing (like we used to write our rich client apps)
    - a rich client interface based on Spring Rich
    - a mobile interface for a pocket PC


    Domain Object:

    The domain object classes represent business entities, the business attributes and business relationships to other entities. This is often a set of relatively simple POJOs. The pet clinic classes (Owner, Pet, Visit, Vet, PetType and Specialty in package org.springframework.samples.petclinic) represent the domain objects in the rich client sample application. Rules that are inherent to an entity can be implemented directly by the domain object class. This includes simple get/set of attributes such as setName(…), as well as relationships such as owner.addPet( aPet ).

    Note that the term ‘domain model’ is sometimes used to mean the Domain Objects themselves. I tend to think of the ‘domain model’ as a synonym for the business tier, which is the collection of all domain objects, services and business rules that are independent of the presentation tier.

    Domain Services
    The domain services represent business operations that involve the domain objects but may be external to the object itself. A simple but common example is ownerService.getOwnerById( ownerId ), or ownerService.save( anOwner ). A more complex business operation could be ownerService.checkAccountIsInGoodStanding( anOwner ).

    Again, note that these service operations are completely independent of the presentation tier (a rich client, or web client application) as the exact same call should be made by either presentation technology.

    Validation / Business Rules:
    The business rules are constraints that the domain objects must satisfy. Many business rules are very simple rules that can be defined for an attribute on a domain object. For example, the owner’s lastName is required, must be <= 255 characters, and be alphabetic. Other business rules require input from two or more attributes (e.g. a date attribute is mandatory only if another condition is true). The rule may be more complex yet and involve multiple domain objects and attributes. (this is the type of business rule that is currently giving us the most trouble).

    Value Objects:
    A value object is a simple ‘bit bucket’ for holding or transporting data. For example, an object to carry search parameters may be a simple value object as it doesn’t really represent a meaningful business entity. Putting all of the query attributes together into a value object to pass to the server is really just a programming convenience.

    It can be somewhat unclear the distinction between a domain model and a value object, and in practice the distinction may not matter that much. For me I tend to think of an object that is just carrying data as a value object, and one that is capturing meaningful business data and relationships as a domain object. In either case, a value object or domain model could be passed to services, and serialized to be passed to/from the server from a remote client.

    Client/Server:
    I would like to define ‘client server’ in the way that traditional client-server tools such as PowerBuilder or VisualBasic work. In fact, many of the Java automatic DB binding tools and GUI builders provided by IDEs are in this category too. This definition may not be completely correct, but I can’t think of a better name or way of describing it. Client-Server applications work by processing data result sets and rows. They typically have an easy way to connect to a database and bring back rows, display them, and insert, update, and delete them.

    With this definition, I would say that a client-server application does not have a true domain model. The system may create Java classes (value objects) to carry around a row, and it may put these objects in a collection, but it is inherently still just result set processing. There has been no attempt to capture the business meaning and relationships other than what the database schema itself represents.

    For many systems or portions of a system, simple result set processing is sufficient and in fact is the appropriate way of developing the application. It starts to break down however when the business domain is complex. A client-server based application often ends up putting a lot of logic that is really domain knowledge in the presentation tier, since a true domain model with domain objects and domain services doesn’t exist.

    Domain Driven Design
    I believe that the potential of Spring Rich Client is that it can directly work with systems that are built around a true domain model. All complex systems need this.

    A system that is driven by a domain model is fundamentally different than a client-server application. The domain model is designed to capture the business entities, the business relationships, core operations and rules. The fact that some entities may be persisted into a relational database is secondary, and it is very common for the structure of the domain model to be significantly different than the relational database schema.

    A very good book that describes this is ‘Domain Driven Design by Eric Evans’.

    The Spring PetClinic example(s):
    Now that I have defined what I mean by some of the basic concepts, I’d like to take a look at the domain model provided with the Spring Pet Clinic sample applications. The Pet Clinic sample provided by spring does provide domain objects, that I believe are true domain objects. These classes, summarized below, are used by both the web and rich client samples, which supports the idea that they are part of the business tier and independent of the presentation tier.

    Code:
    package org.springframework.samples.petclinic;
    
    public class Person extends Entity 
    &#123;
        private String firstName;
        private String lastName;
        private String address;
        private String city;
        private String telephone;
    
        ... getters / setters omitted ...
    &#125;
    
    public class Owner extends Person 
    &#123;
        private Set pets;
    
     ... getters/setters omitted ...
     
        public List getPets&#40;&#41; &#123; ... &#125; // returns an unmodifiable list
        public void addPet&#40;Pet pet&#41; &#123; ... &#125;
        public Pet  getPet&#40;String name&#41; &#123; ... &#125;
        public Pet  getPet&#40;String name, boolean ignoreNew&#41; &#123; ... &#125;
    &#125;
    
    public class Pet extends NamedEntity 
    &#123;
        private Date    birthDate;
        private PetType type;
        private Owner   owner;
        private Set     visits;
    
        ... getter/setters omitted ...
    
        public List getVisits&#40;&#41; &#123; ... &#125; // returns an unmodifiable list
        public void addVisit&#40;Visit visit&#41; &#123; ... &#125;
    &#125;
    
    public class Visit extends Entity 
    &#123;
        private Date    date;
        private String  description;
        private Pet     pet;
     
        ... getters/setters omitted ...
    &#125;
    What does this domain model tell us about the business:
    - An owner is a ‘person’ that has basic information (name, address and phone)
    - An owner can have many pets, and these are identified by the pet’s name
    - There will be trouble if the owner has more than one pet with the same name, but this may be OK with the business (my wife’s sister has had two different dogs with the same name though, so this is likely a problem that the business users missed)
    - A pet has a name, date of birth and a ‘PetType’, which in this simple example can be any string. In a more realistic example it would likely be a code value.
    - A Pet know who it’s owner is (as a direct link – more on this later)
    - Pets can know what visits they have had (list of visits)
    - A visit is on a specific date, for a specific Pet (direct link back to the Pet)

    This is a simple example, and of course it isn’t really meant to be realistic, but based on this domain model, I would want clarify if the business really intends a visit to only be able to handle a single pet. The way this is modeled, an owner with 2 dogs can not take them both to the same visit. (maybe this is really an ‘appointment’ that is tied to an owner who pays the bill, and many ‘treatments’ that is tied to the Pet(s) that were at the appointment) This is just an example of the value of using a domain model – it gives the technical and business users a common understanding of the business problem, and forces the business relationships to be specified in a very precise way.

    Unfortunately, the above domain objects is where the PetClinic sample application stops in terms of a common business tier. The rest of what I believe should be part of the business tier seem to be intermixed with the presentation tier code in the various examples. I don’t think this is necessary, but it is very easy to accidentally do when the application is focusing (for now) on a single presentation technology.

    Validation
    There are validation rules that should also be part of the domain model. In the rich client pet clinic example, these rules are defined in the following class:

    Code:
    package org.springframework.richclient.samples.petclinic.domain;
    
    ... imports deleted ...
    
    public class PetClinicValidationRulesSource extends DefaultRulesSource &#123;
    
        public PetClinicValidationRulesSource&#40;&#41; &#123;
            super&#40;&#41;;
            addRules&#40;createOwnerRules&#40;&#41;&#41;;
        &#125;
    
        private Rules createOwnerRules&#40;&#41; &#123;
            return new Rules&#40;Owner.class&#41; &#123;
                protected void initRules&#40;&#41; &#123;
                    add&#40;"firstName", getNameValueConstraint&#40;&#41;&#41;;
                    add&#40;"lastName",  getNameValueConstraint&#40;&#41;&#41;;
                    add&#40;not&#40;eqProperty&#40;"firstName", "lastName"&#41;&#41;&#41;;
                    add&#40;"address", required&#40;&#41;&#41;;
                &#125;
    
                private Constraint getNameValueConstraint&#40;&#41; &#123;
                    return all&#40;new Constraint&#91;&#93; &#123; required&#40;&#41;, 
                                                  maxLength&#40;25&#41;, 
                                                  regexp&#40;"&#91;a-zA-Z&#93;*", 
                                                  "alphabetic"&#41; &#125;&#41;;
                &#125;
            &#125;;
        &#125;
    &#125;
    Please humour me as I outline the way this is used in the rich client application – it is somewhat round-about, and took quite a bit of time to track down how this was working at all…

    The PetClinicValidationRulesSource has buried inside it the ‘createOwnerRules’ method that fairly nicely encapsulates business rules for the Owner domain object. However, these rules are inherent to the Owner, and should not be tied directly to the presentation technology. The PetClinicValidationRulesSource is defined as a ‘rulesSource’ bean in the rich-client-application-context.xml file. The ‘rulesSource’ bean in turn is used by the ApplicationServices object, which uses this name by default and loads it from the application context. (it isn’t specified in a configuration file – this is a magic name). Note that the ApplicationServices object contains other things like the imageSource, lookAndFeelConfigurer, componentFactory, and other things that are a necessary part of the rich client tier. Unfortunately though, this means that a core operation like applying validation logic is not handled by the business tier – you need to have the rich client specific class available before the validation rules can be used. (the web sample application has a similar, but different technique of applying similar but probably different business rules – I was too lazy to track the web usage of a business rule down)

    Note that the ApplicationServices object in turn is held by the ‘Application’ object (based on a default name – not in a configuration file). The Application object represents the rich client application itself, and is a singleton to hold resources needed throughout the application (like the applicationContext and ApplicationServices).

    Note that the rich client sample application is not using the SpringFramework Validator and Errors interfaces. I would like to investigate why there are different techniques in play for this.

    Validation Questions:

    How should the business tier handle the business rules? We need to make it available directly from the business tier, without any ties to the presentation tier.
    - Should the domain object itself hold its ‘Validator’?
    - Should there be a service object corresponding to each domain object that holds the validator?
    - Should we have a DomainObjectFactory that uses Spring to serve up the domain objects? This could be useful, as Spring could be configured to wrapper the domain objects with the capability of holding the business rules (Validator), Errors object, and other useful things such as automatic dirty checking.

    Should the domain object be able to report any validation errors that exist at the time? Again, this is a question that should be independent of a presentation tier. If the owner.lastName is mandatory but not filled in, a field level error exists for lastName. How a given presentation tier displays this error is not important at the domain model level.

    What object holds the errors object? Note that a Validator for a domain object can be stateless and could therefore be held by a service object. However, an Errors object would need to hold context information about the specific domain object that has errors, so I believe it needs to be held by the domain object itself.

    If the domain object is holding an Errors object, should it also hold a reference to the Validator used to generate the errors? Should the Errors object hold the reference to the Validator that was used?

    Should the business rules for an object (such as Owner) be in their own class? For example, we could break the above example out into:

    Code:
    public class OwnerRules extends BusinessRules
    &#123;
        public OwnerRules&#40;&#41; &#123;
            super&#40;&#41;;
            
            initRules&#40;&#41;;
        &#125;
    
    	protected boolean supports&#40;Class  clazz&#41; &#123; ... &#125;
    	public    void    validate&#40;Object obj, Errors errors&#41; &#123; ... &#125;
    
       protected void initRules&#40;&#41; &#123;
           add&#40;"firstName", getNameValueConstraint&#40;&#41;&#41;;
           add&#40;"lastName",  getNameValueConstraint&#40;&#41;&#41;;
           add&#40;not&#40;eqProperty&#40;"firstName", "lastName"&#41;&#41;&#41;;
           add&#40;"address", required&#40;&#41;&#41;;
        &#125;
    
        private Constraint getNameValueConstraint&#40;&#41; &#123;
            return all&#40;new Constraint&#91;&#93; &#123; required&#40;&#41;, maxLength&#40;25&#41;, regexp&#40;"&#91;a-zA-Z&#93;*", "alphabetic"&#41; &#125;&#41;;
        &#125;
    &#125;
    More complex validations
    Now that we have discussed the simple cases, lets take a look at a more complex business rule. This type of rule is very common in real business systems.

    Lets add the rule that the Visit date must be greater than or equal to the Pet’s date of birth. (you can not have a vet clinic visit for a pet that has not been born yet – you may have a visit for the mother, but not for the unborn puppy) Simple enough, and a very reasonable data integrity check for the business data.

    How do we specify this? I think we should be able to do something like this (I have never written this code for real, so I suspect it won’t even compile):

    Code:
    public class VisitRules extends BusinessRules
    &#123;
        public VisitRules&#40;&#41; &#123;
            super&#40;&#41;;
            
            initRules&#40;&#41;;
        &#125;
    
        protected boolean supports&#40;Class  clazz&#41; &#123; ... &#125;
        public    void    validate&#40;Object obj, Errors errors&#41; &#123; ... &#125;
    
       protected void initRules&#40;&#41; &#123;
           add&#40;"date", geProperty&#40; "date", "pet.birthDate" &#41;;
        &#125;
    &#125;
    What I am trying to specify is the following:
    - there is a business rule tied to the Visit domain object’s ‘date’ field
    - the ‘date’ value must be greater than or equal to the pet’s birthdate (‘pet.birthDate’ will be converted to myVisit.getPet().getBirthDate() using the underlying bean utilities mechanism)

    I think the above should work, however, I am a bit concerned that this has now coupled the ‘Visit’ object with details of the Pet object. Is this OK, or does it violate the encapsulation of the Visit object? Since ‘Visit’ was designed with a back pointer to ‘Pet’ I believe this linkage in the business rule is fine. (more on this later though)

    Using the above technique we should be able to specify fairly complex business rules as long as it is possible to use traversal notation to get to the input data needed by the rule. We want to avoid traversal that is more than one level deep though, as this creates linkages to structures such that modifying the internals of an object could break code in a seemingly unrelated business rules. (Don’t specify something like “pet.owner.lastName? to get the last name of the pet. If we change owner.lastName to owner.familyName, then this rule will break, and the compiler won’t find it.)

    A more realistic domain model
    I would like to try to make the pet clinic example more closely mimic a real life domain model. Let’s consider the following situation:

    We have an owner – let’s call her “Granny Smith?
    Granny Smith is 93 years old and has been at the clinic for the last 50 years.
    During that time, Granny has owned 15 cats, 14 dogs, and one bird (who quickly was eaten by a cat, so she didn’t get any more birds)
    Each of these pets visits the clinic three times per year (each independently because that is how our domain model works)

    What do we have?
    - 1 owner object (Granny Smith)
    - That holds a collection of 30 pets
    - That in turn hold about 30 visits each (average 10 year lifespan with 3 visits per year), for a total of approximately 900 visits
    - (what a good client Granny has been)

    Now, we really don’t want to bring all of this into memory just because Granny shows up with her a new cat for the first visit.

    To solve this, we probably want to decouple the domain models somewhat, and move some functionality from the domain object into a corresponding domain service object. For example, ‘Owner’ could be modified to hold a ‘PetSummary’ record, that is just the basic information about a pet (name and a unique business level pet identifier), instead of the full information. When we call ‘OwnerService.getOwner()’, it would return just the owner record with enough pet information to display the list of pets, but not have all information in memory. When an application selects a specific pet summary record (from a list or tree – from the web application link – whatever, the domain model doesn’t care) it would result in the following call at the domain level ‘PetService.getPetDeails( petIdentifier )’. We may, or may not, attach this pet details record directly to the corresponding pet summary record.

    Similarly, to handle the Visits for a Pet, there could be a service object (PetService, and VisitService) that handle the details of pulling back just the right amount of information, such that we don’t have to bring everything into memory at once, just the portion we need.

    Some Nasty Details
    The back pointers can cause real grief for several reasons:
    - Having a back pointer, such as pet.getOwner() or visit.getPet(), means that the pet or visit objects are directly tied to their parent. We may not want to bring in this object, or we may want to use this object in a different context.
    - Often the lifecycle of the objects are different. In my modified example above, we want to save or update an Owner record without saving the Pet record. We certainly want to update a Pet record without updating the Owner record.
    - In a rich client (remote) application, this is critical, as when we pass a Pet domain object to the server to be saved, we do not want to also pass the ‘Owner’ and in turn all other Pet records and their 900 visits. This is simply too much data!
    - In a web application this is also important, although often overlooked. A web application lives on the server, so a large interconnected object model doesn’t need to be serialized over a slow network just to send it to the data tier to be saved. However, it is very easy to end up pulling too much information into memory, using unnecessary memory and time resources. This of course can kill a web server that needs to handle many simultaneous users.

    Lets assume that we rework our domain model such that the domain objects only hold direct references to other objects that share the same lifecycle (aggregate objects – see the Domain Driven Design book for details on what I mean here). We will not have back pointers, but there will be appropriate methods in domain service objects such that the information an application may need is easily available.

    Back to Validation
    There is a business rule that the pet’s date of birth must be earlier than the date of the visit. We implemented this using a rule on the Visit object that traversed using the back pointer (from above)…

    add("date", geProperty( "date", "pet.birthDate" );

    However, we may no longer have a ‘pet’ back pointer in the Visit object. I need another way to implement this rule, or a better understanding of how to properly decouple a complex domain model such that we don’t end up pulling too much information into memory at once.

    I think this is more than enough to start. I definitely have more thoughts and details on this subject, but would appreciate comments from people that have also tried to fully decouple the domain model from the presentation tier. In our example this is not just a theoretical exercise, as we have both a rich client and web front end to the exact same business problem (in some cases duplicating the screens almost exactly). The business domain, including validation rules and core operations, must be fully independent from the presentation technologies.

    Jim Leask

  • #2
    I'm very interested in this kind of stuff but after reading your book...I mean post...I'm not sure where to start.

    Validation / Business Rules:
    The business rules are constraints that the domain objects must satisfy. Many business rules are very simple rules that can be defined for an attribute on a domain object. For example, the owner’s lastName is required, must be <= 255 characters, and be alphabetic. Other business rules require input from two or more attributes (e.g. a date attribute is mandatory only if another condition is true). The rule may be more complex yet and involve multiple domain objects and attributes. (this is the type of business rule that is currently giving us the most trouble).
    It seems to me like this is validation overkill. Do you really want to check character length on these things as a domain model validation rule? That seems to me like more of a function of the particular presentation you have in mind or the database schema you're trying to fit. Even if a person giving you requirements says, "I don't want a person's name to exceed 255 characters", it doesn't seem to me that this is definitely a domain model thing. I realize you were just giving an example but please consider that for other cases of very fine grained validation and see if it isn't probably more appropriate or more flexible elsewhere.

    Surely some degree of validation will be required at all levels regardless of a domain driven design.


    - Should there be a service object corresponding to each domain object that holds the validator?
    Eric Evans book

    So, in his book he talks about an aggregate root concept. It seems to me that this concept is related to the types of Service objects you'd have. For example. if the Pet is an aggregate root then there would be a PetService. However, it sounds very possible that a Visit is not an aggregate root that a Visit is in fact a function of a Pet or its owner or both and then you'd not have a Visit service.

    - Should we have a DomainObjectFactory that uses Spring to serve up the domain objects? This could be useful, as Spring could be configured to wrapper the domain objects with the capability of holding the business rules (Validator), Errors object, and other useful things such as automatic dirty checking.
    Definitely let Spring manage the service level objects but why the domain model objects themselves? The chicken comes before the egg (in my book) so doesn't the service first create the object for which it is responsible? For example, if a new Pet comes in, who constructs it? I think it would be a service IFF there is a PetService that registers new pets. If there isn't a pet service and instead just a visit service then maybe some other domain model object creates it.

    Anyway, I just don't see how Spring being involved in the creation of domain model objects(not service objects) is desirable.


    Your post hits on several interesting topics but at variuos times so it's a little hard to jump in. You might want to break that out into a couple of different posts. Consider posting at Fowlers site or sending him an email. Evans has a public email too, I think.[/quote]

    Comment


    • #3
      Yeah - that post was a bit out of control wasn't it. I didn't really know where to start when writing it either, but there are a lot of details that I think are really important on real systems that always get glossed over in the sample code. You highlighted some of them that I missed...

      It seems to me like this is validation overkill. Do you really want to check character length on these things as a domain model validation rule? That seems to me like more of a function of the particular presentation you have in mind or the database schema you're trying to fit. Even if a person giving you requirements says, "I don't want a person's name to exceed 255 characters", it doesn't seem to me that this is definitely a domain model thing.
      You are right - the length of 255 isn't really a domain model rule, it is there because the DB schema imposes a maximum length on this field. That said though, it definitely isn't a presentation level rule either (where most systems put it). All presentation code needs to verify this length to give appropriate user feedback, and we should be able to change the database schema, and the corresponding constraint in one place and have all presentation code react correctly.

      Surely some degree of validation will be required at all levels regardless of a domain driven design.
      I think this is the answer, but I have never seen this done in an example. It seems that there are true domain level rules (constraints defined by the business itself, like mandatory and alphanumeric last name), other rules imposed by the persistence tier (like max length 255), and others that are presentation tier specific (what is a good example here - colour for display or fonts maybe) I think most of the constraints we have are really at the domain or persistence tier because the exact same constraint would need to be enforced regardless of whether it is a web or rich client application.

      So, how do we do this? Set up the business rules as decorators that can be layered? A specific presentation tier would then simply add a decorator with the rules specific to that 'application' to the ones that must be always true for all applications.

      As far as how to break up the service objects, I tend to agree with you that Spring shouldn't have to get involved with serving up the domain objects themselves. I think it would be useful though to break up the Spring PetClinic example into a more distinct domain layer. I'd like to see the domain service objects that can serve up the domain business rules (possibly with the persistence business rules as a decorator), and have domain objects (like Visit) that are not connected with hard pointers.

      Comment

      Working...
      X