Sunday 12 December 2010

Turning DTOs into a domain layer

Following on from this blog: Do WCF Data Contracts have relevance outside of WCF where I talk about using WCF Data Contracts outside of service calls. This is part of a strategy for re-using DTOs for more than just a transport layer and building them for use across all layers.

Obviously you will instantly think about domain object behaviours, Object Oriented Design etc. In my experience you want to separate out the behaviour of a class in different layers. For example you will want to bind your View to a Model in the UI or use persistent objects in the Data layer, additionally you will want to have business behaviours in the domain layer. What if you could share the underlying shape of the data across all of these tiers.

In this article I will talk about reusing DTOs as a domain model. The obvious benefit is that you don't need to maintain 2 sets of objects and a mapping layer between them.

The main premise of this model is to have the DTO model representing the data shape of the domain object and adding behaviours through the use of extension methods to add this behaviour. For example you might have a Person DTO:


    [DataContract]
    public class Person
    {
        [DataMember(IsRequired=true)]
        public string Title { get; set; }

        [DataMember(IsRequired = true)]
        public string FirstName { get; set; }
       
        [DataMember(IsRequired = true)]
        public string LastName { get; set; }

        [DataMember(IsRequired = true)]
        public DateTime DateOfBirth { get; set; }

        [DataMember(IsRequired = true)]
        public Address Address { get; set; }

        [DataMember(IsRequired = true)]
        public PersonSex Sex { get; set; }

        [ContractInvariantMethod]
        private void EnforceInvariant()
        {
            Contract.Invariant(this.DateOfBirth < DateTime.Now);
            Contract.Invariant(!string.IsNullOrWhiteSpace(this.FirstName));
            Contract.Invariant(!string.IsNullOrWhiteSpace(this.LastName));
            Contract.Invariant(!string.IsNullOrWhiteSpace(this.LastName));
            Contract.Invariant(this.Address != null);

            Contract.Invariant(Enum.IsDefined(typeof(PersonSex), this.Sex));
        }
    }

Note that this uses the code contract model that I describe in Combing Code Contracts with DataContracts for better contract expressiveness and quality

To build a domain layer on top of this you can add a set of extension methods like this to add some business logic to the class:

    public static class PersonBusinessLayerExtensions
    {
        public static bool IsOver18(this Person person)
        {
            return (person.DateOfBirth <= DateTime.Now.AddYears(18));
        }

        public static bool LivesInUK(this Person person)
        {
            Contract.Requires(person.Address != null);

            return (string.Compare(person.Address.Country, "UK", StringComparison.OrdinalIgnoreCase) == 0);
        }

        public static bool CanVoteInUKElection(this Person person)
        {
            return (IsOver18(person) && LivesInUK(person));
        }
    }



You can see from this that we've added a bunch of methods which query some semantic value on top of the raw data, but you could easily add some modification of the state.

Remember that you can combine this with the Transaction Script pattern for longer business logic transactions spanning DTOs or other calls.

One of the elements that makes it powerful is that you can add polymorphic behaviour in different layers and protect the business logic from the presentation or database layers or change the behaviour. This is enforced by deploying the appropriate assembly in the layer.


    public static class PersonUIExtensions
    {
        public static string GetFullName(this Person person)
        {
            return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", person.Title, person.FirstName, person.LastName);
        }
    }


A potential disadvantage with this model is polymorphism of behaviours through abstract and virtual methods on the domain classes. However this can still be simulated via inheritance of the DTOs and extension methods on the base classes with extended behaviours on derived classes. This will require different names, but that can be a good thing, as sometimes polymorphism can be abused by overloading the behaviours in ways which aren't described by the method name.

Your thoughts on this approach?

Wednesday 8 December 2010

Sacred Cows? There are no Sacred Cows!

Often on projects there are areas that individuals or teams will tell you are fine, or are things that you shouldn't look at all for very plausable reasons; these are called Sacred Cows.

It could be their favourite developer tool, their source code management system, a particular back-end or front-end system - whatever the tool, you are told that is out of scope of examination.

However, work on enough projects and you soon realise that it is the Sacred Cows that will slow you down or sink your project - that is typically why they are Sacred Cows, everyone knows that they don't work, or don't work well but they are the favorites of someone more powerful in the organisation or they have been burned by other tools.

Never ignore a Sacred Cow!

Having said that, how do you deal with Sacred Cows? It depends on what the Sacred Cow is but above all know about them, understand them inside and out, their weaknesses and problems. Come up with a set of work-arounds, tools, standards, project management techniques, enact those that you are able to and have the others in your back-pocket so that when the issues hit you can quickly mobilise to work around the Sacred Cow.

Think about the projects you have worked on, think about those that have failed or had significant issues, think of the Sacred Cows you have come across and discuss in the comments below.