Defending my project architecture
Today in a meeting we had a big debate about project architecture, anemic domain models, SOA, and other kinds of good stuff. Seeing that I was in the minority arguing against lots of really smart people I should answer the call and defend my ideas. :)
First, a little about the architecture of my project. We are writing a Windows Forms app that talks to a middle-tier using WCF and then saves to a SQL Server database. Our layers go something like this:
User Interface (controllers, views)
Service Agent (helps controllers talk to web services)
—- service boundary —-
Data Access (LINQ to SQL)
LINQ to SQL generates all of our entity objects and these entity objects are used on both sides to the wire. Our web services are not exposed to the public and our Winforms client is the only app consuming them.
Here are the issues that people have with this:
Sharing the entity objects on both sides of the wire violates SOA tenets because it introduces tight coupling between server and client.
Sharing the entity objects on both sides does violate SOA tenets. But in our case, what we’re doing is not SOA! Wikipedia defines SOA as:
Service-Oriented Architecture (SOA) is a software architecture where functionality is grouped around business processes and packaged as interoperable services. SOA also describes IT infrastructure which allows different applications to exchange data with one another as they participate in business processes.
Notice the part about applications exchanging data with one another. We are not exposing our web services to anyone other than our client. We have total control of both sides of the wire. Just because I’m using web services in my app does not mean that we have a service oriented architecture! So why should I create different entity objects for each side of the wire along with DTOs and translation classes? What benefit does that give me?
Sure, sometimes you add properties to entity objects just to help you display something in a grid. And sometimes you have properties on an entity that you will never need on the client side. And theoretically there is a situation where you may want your entity object on the client to be structured differently than they are on the server for some reason. But are any of these good reasons to create new layers and create more code that I have to maintain?
I’ve worked on projects that have had separate entities for the client and server (with DTOs and translation in between). This was quite painful. Every time I had to add a new entity object with corresponding services and UI screens, I had to add 13 files to the project. 13 files!! This was before I even wrote any unit tests. I spent more time writing plumbing than anything else.
One day when I was working said project I picked up what looked like an easy bug. I had to add 2 fields to a database table and then add them to a screen. No fancy logic, no validation. It took me 20 minutes! All I was doing as was adding two properties to a screen! These types of things should be easy.
On my current project, things go much faster. I can drag my database tables onto the LINQ to SQL designer and immediately I have my entity objects created and I can load and save them to the database. I can immediately go and start writing code that has business value. I have to create 4 fewer classes for each entity now (no client entity, no DTOs, no translation code). I’m not hand-writing my entities and mapping files because these are generated for me. Sure, they’re not true POCO entities (although you can do this with LINQ to SQL if you don’t want to use the generated code), but I don’t think that it’s worth throwing away the generated code just so that I can write my own POCO entities (read: plumbing).
There are some cases where we still need DTOs to load and save data in complex situations, but we don’t spend the time doing it until we need to.
Like I said before, all of my web services are only consumed by our client. If another application or someone outside of my team wanted to consume my services, I would create a new service and use DTOs and translation so that I don’t tightly couple my domain model with outside consumers. Now I’m doing true SOA! But I don’t spend the time doing all of the extra work until I need to do it.
Because LINQ to SQL generates our entity objects and we use these throughout the app, we don’t have a rich domain model and our entity objects map one-to-one with database tables.
Sure, I admit that having entity objects map one-to-one with database tables isn’t always “ideal”, and LINQ to SQL doesn’t handle many-to-many relationships that well. But honestly, it’s not that bad. This doesn’t always sit well with the Domain Driven Design crowd, who say that you should start by creating your business objects and that the primary focus should be on the domain and domain logic. Certainly these are good concepts, and I’m not saying that DDD is a bad idea at all. If we were using NHibernate instead of LINQ to SQL we could make our business objects more like business objects and less like database tables.
My point is this — there are situations where Domain Driven Design is good (complex domain designs). But keep in mind that the goal is to deliver a working piece of software (that is testable and maintainable and all that). No one will care that your business layer is super-elegant if you don’t get done on time. In that case, my “good enough” business layer trumps the more elegant business layer that took longer to develop (in fact, I’d guess that with DDD you would still have the majority of your business objects map one-to-one with database tables anyway). Remember, the architecture is not the deliverable.
The business objects do not have any behavior in them, all of that is in the business logic layer (the anemic domain model anti-pattern), and this is not good object-oriented design.
Yes, the fact that my business objects don’t have the logic in them is an example of the anemic domain model pattern. You got me on this one. But really, how bad is this? Sure, you don’t know to look for GetOrdersForCustomer() in the CustomerLogic class or the OrderLogic class. But what’s the worst that can happen? Someone writes the code to fetch this stuff twice?
While I would love to have the business logic encapsulated in my business objects, I also don’t want to send all of the validation rules and server logic over to the client, and I don’t want to switch to DTOs and client entities just to resolve this problem. So I’ll live with the anemic domain model for now. Honestly, most of the complaints I’ve heard is that it’s annoying to have the logic in separate classes, but I haven’t heard anyone say that it flat out doesn’t work.
Remember, what is our goal — to deliver a piece of software on time, that meets the business needs, and is maintainable. On my current project, we delivered on time (our first release was done a month ahead of time), we were able to spend more time writing code that meets a business need (vs. writing plumbing), and our code is quite maintainable in my opinion (18 different developers have worked on it, and we just wrote our 3000th unit test today). Obviously, I cannot take most of the credit for this because the people writing the code are the main reason for the success. But I like to think that the architecture that we’re using helped us do it.