This post originated from an RSS feed registered with .NET Buzz
by Udi Dahan.
Original Post: What's persistence got to do with it ?
Feed Title: Udi Dahan - The Software Simplist
Feed URL: http://feeds.feedburner.com/UdiDahan-TheSoftwareSimplist
Feed Description: I am a software simplist. I make this beast of architecting, analysing, designing, developing, testing, managing, deploying software systems simple.
This blog is about how I do it.
My posts seem to be garnering lots of comments from around the blogosphere. However, many seem to view the posts made so far as a recommendation on how to do SOA. This is not quite true. I'm trying to take you, step by step, from LA - which you already know - to SOA. The journey isn't complete, yet.
What's been covered so far is this:1. How to create a layer/project in .Net that behaves like a service; 2. How to create an entity layer/project containing all the kinds of data that will be passed between layers/services.
These are necessary first steps to take, and are by no means the final solution.
I also gave several examples of classic services - security, logging, exception management, etc.
Steve Maine states in his post that all I've done so far is migrated LA to something called an application framework. If this is in fact true, I'm well pleased with myself. Achieving orthogonality, generality, and reusability in any architecture can be viewed as a success. In his opinion, this falls short of the pure SOA. To tell you the truth, if all you get from all this hoo-hah is how to make orthogonal, general, and reusable architectures, then my daily dollar's been well spent.
I think that it's important to point out at this point that the stages above are indeed prerequisites to SOA. Sure, any idiot can create a webservice that forwards calls to other code without giving a thought to any of the *ilities above ( and a lot of idiots do ) - but that's not SOA. More importantly, none of the benefits of SOA can be achieved. This is why I focus so much on the "small-scale" SOA. Doing "large-scall" SOA while ignoring the small may make for a well-designed large machine, but low-quality gears, drive chains, and cogs won't bear the weight.
So, what are the next steps, you ask. Well, that's an excellent question. There are various ways to proceed, so I'll start from the most understandable steps.
First off, lets see how to do persistence, which, for some reason, was always the sticking point for a lot of developers used to O/R mapping. So, we have a PersistenceService project, which has a one public class called PersistenceService. This class has a private constructor ( nobody needs to initialize this because ... ) and all static methods. All the methods you'd expect to find are still there. Personally, I don't like the catchall "Save" method and prefer having separate "Create" and "Update" methods. All the basic CRUD operations are there for every entity.
I once tried having all entities inherit from an IBaseEntity which pretty much only declared an Id property. This was so that the PersistenceService exposed only one Create, Read, Update, and Delete method - each accepting an IBaseEntity. Then, in the internal service implementation I did a ".GetType()" on the parameter and passed in on to the relevant class. Although this decreased the number of methods in the API, it didn't have as far-reaching benefits as I'd hoped.
Here's some code:
namespace PersistenceService
{
public class PersistenceService
{
public void Create(Entities.Customer c)
{
// calls to logging service omitted
// exception management service calls which wrap the following call omitted
InternalService.Create(c);
}
}
}
Simple, isn't it ?
All calls in the public class look pretty much the same. The InternalService.Create(Entities.Customer c) method can perform the work required or further delegate the work to a Customer class. I assume that you know how to perform the calls necessary to actually insert a row into the database. Just so you know, the "Read" method is actually the most interesting.
For all the O/R guys out there, yes, you handle all this and more. But that's exactly the problem. When it comes to persistence, that's all the should be done. No more. All the interesting stuff happens somewhere else - in a different service. Call it "BusinessService" or whatever you like, 'cause it in essence eliminates the infamous BL.
I have some answers to give to questions that have come up in the comments. How does the PersistenceService return collections, especially in the case when returning all children for a parent ?
From an API perspective, its simple ( yet again ). The Read method which receives an entity as a parameter returns the corresponding collection of entities. eg. public static Entities.OrderCollection Read(Entities.Order o). The internal implementation of this method is more interesting and can take many forms. For instance, if the CustomerId property of the order is set ( not null ), then you would select all orders where the customer id is equal to that in the given order's property. Another case may be that the Id property of the order is set, then you would select the order with the Id equal to that of the given order's property.
The most important thing that I can say about the persistence service is this: No client code should ever deal directly with it, all code should/must go through the business service - which will be the topic of my next post. =)