Question

I am working on a large project which, in part, is to replace an existing server stack. With a large, very normalized database, it was clear that we would need to construct many composite objects. An example of one of my queries is such: My composite object definition:

[DataContract]
public class CompositeEvent
{
    Guid eventIdentity;
    string accountIdentity;
    string eventMessage;
    DateTime eventLoggedOn;
    string methodName;
    string programName;
    string userIdentity;
    List<CompositeException> exceptions = new List<CompositeException>( );
    List<CompositeParameter> methodParameters = new List<CompositeParameter>( );
    List<CompositeParameter> databaseParameters = new List<CompositeParameter>( );

    [DataMember]
    public Guid EventIdentity
    {
        get { return eventIdentity; }
        set { eventIdentity = value; }
    }
    [DataMember]
    public string AccountIdentity
    {
        get { return accountIdentity; }
        set { accountIdentity = value; }
    }
    [DataMember]
    public string EventMessage
    {
        get { return eventMessage; }
        set { eventMessage = value; }
    }

    [DataMember]
    public DateTime EventLoggedOn
    {
        get { return eventLoggedOn; }
        set { eventLoggedOn = value; }
    }

    [DataMember]
    public string MethodName
    {
        get { return methodName; }
        set { methodName = value; }
    }

    [DataMember]
    public string ProgramName
    {
        get { return programName; }
        set { programName = value; }
    }

    [DataMember]
    public string UserIdentity
    {
        get { return userIdentity; }
        set { userIdentity = value; }
    }

    public string QualifiedCreator
    {
        get 
        {
            if ( String.IsNullOrEmpty( programName ) && String.IsNullOrEmpty( methodName ) )
                return string.Empty;
            return string.Format( "{0}:{1}", String.IsNullOrEmpty( ProgramName ) ? "{undefined}" : ProgramName, String.IsNullOrEmpty( MethodName ) ? "{undefined}" : MethodName );
        }
    }
    [DataMember]
    public int EventTypeIdentity { get; set; }

    [DataMember]
    public string EventTypeName { get; set; }

    [DataMember]
    public List<CompositeException> Exceptions
    {
        get { return exceptions; }
        set { exceptions = value; }
    }

    [DataMember]
    public List<CompositeParameter> MethodParameters
    {
        get { return methodParameters; }
        set { methodParameters = value; }
    }

    [DataMember]
    public List<CompositeParameter> DatabaseParameters
    {
        get { return databaseParameters; }
        set { databaseParameters = value; }
    }
}

And my database query in my service :

            using ( Framework.Data.FrameworkDataEntities context = new Data.FrameworkDataEntities( ) )
        {
            var list = from item in context.EventLogs
                       join typeName in context.EventLogTypes on item.EventTypeId equals typeName.Id
                       orderby item.EventLoggedOn, item.EventLogType
                       select new CompositeEvent
                       {
                           AccountIdentity = item.AccountIdentity,
                           EventIdentity = item.Id,
                           EventLoggedOn = item.EventLoggedOn,
                           EventMessage = item.EventMessage,
                           EventTypeIdentity = item.EventTypeId,
                           EventTypeName = typeName.EventTypeName,
                           MethodName = item.MethodName,
                           ProgramName = item.ProgramName,
                           UserIdentity = item.UserIdentity
                       };
            return new List<CompositeEvent>( list );
        }

Now it is clear from the definition of my composite object that it only contains data, no business rules, and no other persistence or exposure of business structure, data structure, or anything else.

Yet my team mate states that this can only exist in the domain model and I MUST spend cycles moving my data from this well-defined object to a DTO. Not only that but a) all DTO's must have DTO in their name and b) they must only exist for the purpose of being moved across the network. So when our client code is developed it must a) create properties of each object property and b) move from the DTO to the view model properties.

I feel that this is taking DTO to the extreme. That based on the definition of the composite objects that these ARE data transfer objects. Also I see absolutely no issue with storing this object in the client and doing bindings directly to this object in the client. Since all I'm doing is taking every property defined in the composite object and copying them into the DTO it seems to be a massive waste of time and cycles to create an object only to copy it to another object then copy it once again to internal properties of the client.

I'm interested in other opinions on this. I feel that I'm not violating any of the OOP rules such as Separation of Concerns or Single Responsibility Principle ... at least as long as you don't become extremely anal about these. Opinions????

Was it helpful?

Solution

Well, all MUSTs your colleague emphasized make him seem a bit OCD. But that's OK, because he does have a point.

The DTO's, especially in MVC, should be designed in such a way that they abstract the underlying data model, so they become the actual model for the view. Keeping them to close to your object will only cause you to modify both sets of objects when maybe only one of them actually needs to change.

Another thing to take into account when designing DTOs in SoA or N-tier architectures is that they should not create a contract between your view layer and the underlying services and model. So if something changes in your database (a field, a table, a view, etc), you should be able to adjust everything from your services layer and lower in such a way that the DTOs are not affected.

The view model should stay the same, at least as long as you don't add or remove functionality from it. Any underlying changes should be adapted to the DTOs (not the way around - but it's not always possible).

Now I should mention that I'm now switching a project from SoA and DTO's to a plain 2-tier architecture (it was because of performance issues, not DTO's :)). I have a cross-cutting domain layer of both simple and complex objects, designed to be used only internally. Any exposed API's over web services will use DTO's indeed, since I don't want to kill the WS consumers. Maybe hurt them a bit :).

But you should go with DTOs, the way I mentioned above. It will help in your situation (with MVC). For easier mapping to DTO's and the other way around I suggest AutoMapper. Will make your life a lot easier (and your colleague happy).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top