views:

333

answers:

5
+4  Q: 

ORM and layers

Sorry for this point being all over the place here...but I feel like a dog chasing my tail and I'm all confused at this point.

I'm trying to see the cleanest way of developing a 3 tiered solution (IL, BL, DL) where the DL is using an ORM to abstract access to a DB.

Everywhere I've seen, people use either LinqToSQL or LLBLGen Pro to generate objects which represent the DB Tables, and refer to those classes in all 3 layers. Seems like 40 years of coding patterns have been ignored -- or a paradigm shift has happened, and I missed the explanaition part as to why its perfectly ok to do so.

Yet, it appears that there is still some basis to desiring being data storage mechanism agnostic -- look what just happened to LinqToSQL: a lot of code was written against it -- only for MS to drop it... So I would like to isolate the ORM part as best I can, just don't know how.

So, going back to absolute basics, here are the basic parts that I wish to have assembled in a very very clean way:

The Assemblies I'm starting from: UL.dll BL.dll DL.dll

The main classes:

A Message class that has a property exposing collection (called MessageAddresses) of MessageAddress objects:

class Message 
{
    public MessageAddress From {get;}
    public MessageAddresses To {get;}
}

The functions per layer:

The BL exposes a Method to the UI called GetMessage (Guid id) which returns an instance of Message.

The BL in turn wraps the DL.

The DL has a ProviderFactory which wraps a Provider instance. The DL.ProviderFactory exposes (possibly...part of my questions) two static methods called GetMessage(Guid id), and SaveMessage(Message message) The ultimate goal would be to be able to swap out a provider that was written for Linq2SQL for one for LLBLGen Pro, or another provider that is not working against an ORM (eg VistaDB).

Design Goals: I would like layer separation. I would like each layer to only have dependency on layer below it, rather than above it. I would like ORM generated classes to be in DL layer only. I would like UL to share Message class with BL.

Therefore, does this mean that:

a) Message is defined in BL b) The Db/Orm/Manual representation of the DB Table ('DbMessageRecord', or 'MessageEntity', or whatever else ORM calls it) is defined in DL. c) BL has dependency on DL d) Before calling DL methods, that do not have ref or know about BL, the BL has to convert them BL entities (eg: DbMessageRecord)?

UL:

Main() 
{
    id = 1;
    Message m = BL.GetMessage(id);
    Console.Write (string.Format("{0} to {1} recipients...", m.From, m.To.Count));
}

BL:

static class MessageService 
{ 
    public static Message GetMessage(id)
    {
        DbMessageRecord message = DLManager.GetMessage(id);
        DbMessageAddressRecord[] messageAddresses = DLManager.GetMessageAddresses(id);

        return MapMessage(message, 
    }

    protected static Message MapMessage(DbMessageRecord dbMessage. DbMessageAddressRecord[] dbAddresses)
    {
        Message m = new Message(dbMessage.From);
        foreach(DbMessageAddressRecord dbAddressRecord in dbAddresses){
        m.To.Add(new MessageAddress (dbAddressRecord.Name, dbAddressRecord.Address);
    }
}

DL:

static class MessageManager 
{
    public static DbMessageRecord GetMessage(id);
    public static DbMessageAddressRecord  GetMessageAddresses(id);
}

Questions: a) Obviously this is a lot of work sooner or later. b) More bugs c) Slower d) Since BL now dependency on DL, and is referencing classes in DL (eg DbMessageRecord), it seems that since these are defined by ORM, that you can't rip out one Provider, and replace it with another, ...which makes the whole exercise pointless...might as well use the classes of the ORM all through the BL. e) Or ...another assembly is needed in between the BL and DL and another mapping is required in order to leave BL independent of underlying DL classes.

Wish I could ask the questions clearer...but I'm really just lost at this point. Any help would be greatly appreciated.

+2  A: 

that is a little all over the place and reminds me of my first forays into orm and DDD. I personally use core domain objects, messaging objects, message handlers and repositories. So my UI sends a message to a handler which in turn hydrates my objects via repositories and executes the business logic in that domain object. I use NHibernate to for my data access and FluentNHibernate for typed binding rather than loosy goosey .hbm config.

So the messaging is all that is shared between my UI and my handlers and all BL is on the domain.

I know i might have opened myself up for punishment for my explanation, if its not clear i will defend later.

Personally i am not a big fan of code generated objects.

I have to keep adding onto this answer. Try to think of your messaging as a command rather than as a data entity representing your db. I'll give u an example of one of my simple classes and an infrastructure decision that worked very well for me that i cant take credit for:

[Serializable]
public class AddMediaCategoryRequest : IRequest<AddMediaCategoryResponse>
{
    private readonly Guid _parentCategory;
    private readonly string _label;
    private readonly string _description;

    public AddMediaCategoryRequest(Guid parentCategory, string label, string description)
    {
        _parentCategory = parentCategory;
        _description = description;
        _label = label;
    }

    public string Description
    {
        get { return _description; }
    }

    public string Label
    {
        get { return _label; }
    }

    public Guid ParentCategory
    {
        get { return _parentCategory; }
    }
}

[Serializable]
public class AddMediaCategoryResponse : Response 
{
    public Guid ID;
}


public interface IRequest<T> : IRequest where T : Response, new() {}


[Serializable]
public class Response
{
    protected bool _success;
    private string _failureMessage = "This is the default error message.  If a failure has been reported, it should have overwritten this message.";
    private Exception _exception;

    public Response()
    {
        _success = false;
    }

    public Response(bool success)
    {
        _success = success;
    }

    public Response(string failureMessage)
    {
        _failureMessage = failureMessage;
    }

    public Response(string failureMessage, Exception exception)
    {
        _failureMessage = failureMessage;
        _exception = exception;
    }

    public bool Success
    {
        get { return _success; }
    }

    public string FailureMessage
    {
        get { return _failureMessage; }
    }

    public Exception Exception
    {
        get { return _exception; }
    }

    public void Failed(string failureMessage)
    {
        _success = false;
        _failureMessage = failureMessage;
    }

    public void Failed(string failureMessage, Exception exception)
    {
        _success = false;
        _failureMessage = failureMessage;
        _exception = exception;
    }
}


public class AddMediaCategoryRequestHandler : IRequestHandler<AddMediaCategoryRequest,AddMediaCategoryResponse>
{
    private readonly IMediaCategoryRepository _mediaCategoryRepository;
    public AddMediaCategoryRequestHandler(IMediaCategoryRepository mediaCategoryRepository)
    {
        _mediaCategoryRepository = mediaCategoryRepository;
    }

    public AddMediaCategoryResponse HandleRequest(AddMediaCategoryRequest request)
    {
        MediaCategory parentCategory = null;
        MediaCategory mediaCategory = new MediaCategory(request.Description, request.Label,false);
        Guid id = _mediaCategoryRepository.Save(mediaCategory);
        if(request.ParentCategory!=Guid.Empty)
        {
            parentCategory = _mediaCategoryRepository.Get(request.ParentCategory);
            parentCategory.AddCategoryTo(mediaCategory);
        }
        AddMediaCategoryResponse response = new AddMediaCategoryResponse();
        response.ID = id;
        return response;
    }
}

I know this goes on and on but this basic system has served me very well over the last year or so

you can see that the handler than allows the domain object to handle the domain specific logic

Brandon Grossutti
Hi Painless:Could I trouble you to stub out the above in terms of C# classes so that I can see a bit more clearly what you are suggesting? Thanks!
Thanks for expanding on that.Ok. So instead of passing a DTO/POCO/represention of table, you're passing a message structure...somewhat akin to an EventArgs package, but a CommandArgs package, containing (in my case) maybe the Guid of the Message record I want back. You then send back a ResponseArgs package, containing the XXX.The XXX is in your simple case, an Value Type (ID) -- but when returning stuff for a Message, are we returning a DbMessageRecord? Or would you flatten it out into parameters (no dependency), to be reassembled into Message in BL? Or use a DTO defined elsewher?
I will get back when back at the office tomorrow and give you a proper layout here
Brandon Grossutti
so the concept for this is todo context specific messages. State your intent in your message, the handler is completely specific to the intent of the message and in this case is a request response engine. so when i add a new record in this case a mediacategory I send back the guid to the UI, web app etc, which it then can go ask for the item in another message GetMediaCategyRequest.
Brandon Grossutti
The ORM portion hydrates a domain object from its repository which is injected into the class, using something like castle, I have a start up program that finds all dependent assemblies loads them then injects their dependencies and registers all the handlers for the application so that when a message comes down the pipe, it knows where to send it. Once the domain object is hydrated from the repository using NHibernate (as you can see i use interfaces for all repositories so I can refactor out NHibernate if need be)
Brandon Grossutti
Once the domain object is hydrated i then execute all business logic there. The Guid response is merely something i always do when creating a new object so that my UI app has a reference to something that was created in my domain so that if it needs a DTO (message) of the object it can get access to it.
Brandon Grossutti
In closing this level of seperation is really what has helped me become very effective the last year. I build alot client server apps and esb like interaction so this allows me to have an strong infrastructure that i can take from project to project, i have also moved these things to different transports and different persistence layers with little to no issue whatsoever. Great thing aabout these serialized messages is that everything is recreateable if need be and makes for easier testing.
Brandon Grossutti
My #1 rule that was taught to me a long time ago and makes alot of sense, dont worry about your DB, it can take any shape you want just because you have a an object with 2 strings doenst necessarily mean your db does. All applications / model should be persistent ignorant dont let a db define your domain. as Jim said below Martin Fowlers book is fantastic as well as Jimmy Nilsson and Eric Evans, they changed the way i programmed, and also with a lothelp from Greg Young along the wayGood luck Ciel
Brandon Grossutti
A: 

Try centralizing all data access using a repository pattern. As far as your entities are concerned, you can try implementing some kind of translation layer that will map your entities, so it won't break your app. This is just temporary and will allow you to slowly refactor your code.

obviously I do not know the full scope of your code base so consider the pain and the gain.

CodeToGlory
The repository should be defined in BL, or DL? And return DbMessage or Message objects? Currently (after looking up def of Repository Pattern) it appears that my Providers are very similar to that pattern, providing methods that hide any ref to Query method, just requiring parameters, and returning result that is not related to underlying storage mechanism. The only question -- again, is what are they returning, and where are they mapped back and forth...or not?
+1  A: 

This is probably too indirect an answer, but last year I wrestled with these sorts of questions in the Java world and found Martin Fowler's Patterns of Enterprise Application Architecture quite helpful (also see his pattern catalog). Many of the patterns deal with the same issues you're struggling with. They are all nicely abstract and helped me organize my thinking to be able to see the problem at a higher level.

I chose an approach that used the iBatis SQL mapper to encapsulate our interactions with the database. (An SQL mapper drives the programming language data model from the SQL tables, whereas an ORM like yours goes the other way around.) The SQL mapper returns lists and hierarchies of Data Transfer Objects, each of which represents a row of some query result. Parameters to queries (and inserts, updates, deletes) are passed in as DTOs too. The BL layer makes calls on the SQL Mapper (run this query, do that insert, etc.) and passes around DTOs. The DTOs go up to the presentation layer (UI) where they drive the template expansion mechanisms that generate XHTML, XML, and JSON representations of the data. So for us, the only DL dependency that flowed up to the UI was the set of DTOs, but they made the UI a lot more streamlined than passing up unpacked field values would.

If you couple the Fowler book with the specific help other posters can give, you'll do fine. This is an area with a lot of tools and prior experience, so there should be many good paths forward.

Edit: @Ciel, You're quite right, a DTO instance is just a POCO (or in my case a Java POJO). A Person DTO could have a first_name field of "Jim" and so on. Each DTO basically corresponds to a row of a database table and is just a bundle of fields, nothing more. This means it's not coupled closely with the DL and is perfectly appropriate to pass up to the UI. Fowler talks about these on p. 401 (not a bad first pattern to cut your teeth on).

Now I'm not using an ORM, which takes your data objects and creates the database. I'm using an SQL mapper, which is just a very efficient and convenient way to package and execute database queries in SQL. I designed my SQL first (I happen to know it pretty well), then I designed my DTOs, and then set up my iBatis configuration to say that, "select * from Person where personid = #personid#" should return me a Java List of Person DTO objects. I've not yet used an ORM (Hibernate, eg, in the Java world), but with one of those you'd create your data model objects first and the database is built from them.

If your data model objects have all sorts of ORM-specific add-ons, then I can see why you would think twice before exposing them up to the UI layer. But there you could create a C# interface that only defines the POCO get and set methods, and use that in all your non-DL APIs, and create an implementation class that has all the ORM-specific stuff in it:

interface Person ...

class ORMPerson : Person ...

Then if you change your ORM later, you can create alternate POCO implementations:

class NewORMPerson : Person ...

and that would only affect your DL layer code, because your BL and UI code uses Person.

@Zvolkov (below) suggests taking this approach of "coding to interfaces, not implementations" up to the next level, by recommending that you can write your application in such a way that all your code uses Person objects, and that you can use a dependency injection framework to dynamically configure your application to create either ORMPersons or NewORMPersons depending on what ORM you want to use that day

Jim Ferrans
Hi Jim:I have PEAA, right here by my desk (lent by a co-worker last week): I've been mostly lost with it. Simple patterns I get, but I'm findind out that I'm not very good with too much abstraction. I understand code...not patterns yet. *which is the source of the problem I am in ...trying to get enough patterns uunderstood to get back to writing code :-)As for what you wrote... Hold on...The DTO's are just classes that represent the tables, (POCOs) right? So you're using them all the way up to the UI layer...is somewhat the same as using the classes generated by an ORM: no spearation?
Or were you mapping between Orm generated classes and DTOs? And if so, in the BL, or DL? (what depependicies, and in what direction?)
A: 

My opinion only, YMMV.

When I'm messing with any new technology, I figure it should meet two criteria or I'm wasting my time. (Or I don't understand it well enough.)

  1. It should simplify things, or worst case make them no more complicated.

  2. It should not increase coupling or reduce cohesiveness.

It sounds like you feel like you're headed in the opposite direction, which I know is not the intention for either LINQ or ORMs.

My own perception of the value of this new stuff is it helps a developer move the boundary between the DL and the BL into a little more abstract territory. The DL looks less like raw tables and more like objects. That's it. (I usually work pretty hard to do this anyway with a little heavier SQL and stored procedures, but I'm probably more comfortable with SQL than average). But if LINQ and ORM aren't helping you with this yet, I'd say keep at it, but that's where the end of the tunnel is; simplification, and moving the abstraction boundary a bit.

le dorfier
Hi Le Dorfier:It should get easier...but a lot of things are easy...but not good coding. I just havn't figured out how ORM's stand in all that. Slipper slope. So easy (with Linq2SQL) but with delayed execution added into the mix, I've got ...no separation of concern. Just ..."Ravioli". :-)
+2  A: 

The concept you seem to be missing is IoC / DI (i.e. Inversion of Control / Dependency Injection). Instead of using static methods, each of your layers should only depend on an interface of the next layer, with actual instance injected into the constructor. You can call your DL a repository, a provider or anything else as long as it's a clean abstraction of the underlying persistence mechanism.

As for the objects that represent the entities (roughly mapping to tables) I strongly advise against having two sets of objects (one database-specific and one not). It is OK for them to be referenced by all three layers as long as they are POCOs (they should not really know they're persisted), or, even DTOs (pure structures with no behavior whatsoever). Making them DTOs fits your BL concept better, however I prefer having my business logic spread across my domain objects ("the OOP style") rather than having notion of the BL ("the Microsoft style").

Not sure about Llblgen, but NHibernate + any IoC like SpringFramework.NET or Windsor provide pretty clean model that supports this.

zvolkov
agree completely
Brandon Grossutti