SOA, Messages, DTOs and Mappers

OK, I’m currently in the middle of refactoring the current architecture. I’ve been reading about Messages, DTOs (Data Transport Objects) and SOA (Service Oriented Architecture). What I’ve been reading makes sense but I’m finding it hard to locate some really good examples of how it should be done. So I’m going to outline my current understanding and initial idea of what I’m going to do. Hopefully somebody with some experience in this field can give me a few pointers.

SOA

Service methods should be course grained which minimize round trips from the client. To my way of thinking a client could be remote and communicates with our services via web-services or a local web/windows application that calls services directly. So in our application, the client is our MonoRail web application where the controllers call the injected services directly. In a Service Oriented Architecture our services should model real world business processes rather than domain entities. I must admit that I sometimes find it hard to name services correctly so they aren’t named after domain entities, but I guess this will come with more experience with SOA.

DTOs

When I first encountered the idea of DTOs I didn’t really understand the point of them. I thought, why can’t I just pass up the domain objects through the layers up to the front-end? After all, I was always taught that I should reuse code and use the DRY principle (Don’t Repeat Yourself). DTO’s by their very nature introduce code duplication, which seemed all wrong.

I have now seen the light and understand their benefits which out way the code duplication issue. Their benefits include:

  • Minimizing data transferred to the client which improves performance all around especially for remote clients which access services over the network (via web-services or even Ajax calls).
  • Keeps things clean by only exposing the required data for the given service call which also improves serialization especially when a collection is marked as lazy with ORM.
  • Stops unexpected data persistence when using ORM such as NHibernate/ActiveRecord when the session flushes at the end of the request.
  • Improves security when data binding requests (as we do in MonoRail) where a HTTP request could be created to modify data even though it is not available in the front-end.
  • Isolates the front-end from changes in the domain.

Ben has a more detailed post about the advantages of using DTOs which is well worth a read.

Mappers

Mappers are pretty simple classes that the service uses to map from a domain entity to a DTO and back again. The mappers that I’m using will map from a particular domain entity to a particular DTO or collection of DTOs. Currently I’m doing this “manually”, but I’m thinking about ways to do this automatically using reflection and matching on properties that match by name and type. More on this in a later post if I still think it is a possible solution.

Messages

Depending on what you read, the terms Messages and DTOs seem to be interchangeable. However I think there is a difference. I’m currently thinking (which may be wrong) that a service call should accept a request message and should return a response message. Both should be unique to the service call as they are course grained. These messages can contain domain DTOs which are tailored to the particular usage. The DTO’s can be reused in multiple service calls.

I feel an example coming on to help demonstrate my thoughts.

An Example

Imagine we have a User domain entity which comprises an id, first name, last name, email, user name, password, status and permissions list. We also have a UserStatus domain entity which includes an id, name and code. These domain objects would be mapped using an ORM such as NHibernate or ActiveRecord which we want to isolate from the front-end.

public class User
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public UserStatus Status { get; set; }
    public List<string> Permissions { get; set; }
}
public class UserStatus
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public int Code { get; set; }
}

Now imagine in our front-end we want to be able to show a list of users and be able to filter by their status. We might create a UserDTO which is stripped down to only the fields we want to show in the list:

public class UserDTO
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public UserStatusDTO Status { get; set; }
}

We might also create a UserStatusDTO that contains the data required to show a filter with all possible status options:

public class UserStatusDTO
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

Next we need a single service call that will return a list of users and all possible statuses.

public interface IAccountsService
{
    public RetrieveUsersForListingResponse RetrieveUsersForListing(RetrieveUsersForListingRequest);
}

Finally we need two messages which will be unique for the service call, one request and one response:

public class RetrieveUsersForListingRequest
{
    public Guid FilterByStatusId { get; set; }
}
public class RetrieveUsersForListingResponse
{
    public List<UserDTO> Users { get; set; }
    public List<UserStatusDTO> Statuses { get; set; }
}

Obviously we need an implementation for our service interface which I won’t go into now but the service should basically validate the request, call the required repositories, map the domain entities to DTOs and construct the response which is returned. The service now has clear responsibilities.

The benefits here are pretty clear. We have a single service call to return two object lists rather than a separate call for each list. We also stripped down the returned data to only what is required. These benefits are especially useful when calling the service over the network. When called from our MonoRail application (our client) this helps to keep the controllers as thin as possible and allow them to do their job cleanly.

We could extend this example by providing service calls for retrieving and saving a user (for editing). We’d need a new user DTO (for example UserDetailsDTO) which includes the data that a user is allowed to edit and new request and response messages. But I think you get the idea now (and this post is pretty big now).

I’ll be refactoring the current PixelBugs architecture to match this pattern which you can take a look at on Google Code for more examples.

So what do you think? I’d be interested in any thoughts or comments even as a confirmation that I’m on the right lines. Of course I may have completely misunderstood the concepts, if so, please let me know.

Advertisements

4 Responses

  1. Nice post Andy,

    am in a similar situation now. When it comes to ORM and SOA I think we need to separate the concerns and have to live with some mapping(be it via hand crafting) if we want to get the SOA interfaces correct as well as maintain consistency on the DOAs.

  2. Hi,
    I see that you use `User` in [ActiveRecord(“`User`”)]

    Are the “ required so that the generator uses the table name correctly? i tried the same but i get this error at GenerateSchema:

    Message: Incorrect syntax near the keyword ‘User’.
    StackTrace: NHibernate.Tool.hbm2ddl.SchemaExport.Execute()
    NHibernate.Tool.hbm2ddl.SchemaExport.Create()
    Castle.ActiveRecord.ActiveRecordStarter.CreateSchema()

    • The `characters are only required for table names that are reserved words in SQL. So in this case using `User` will escape the table name to [User] in SQL and will generate the script correctly. Other table names such as Post will not require the ` characters.

  3. This is quite a hot info. I think I’ll share it on Twitter.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: