Search code examples
c#entity-frameworkdton-tier-architecture

Are DTOs needed when the entire web app will reside on a single server?


I've started working on a WebForms (not MVC) application that will reside on a single server (database included) and have decided to design it using what (I think) is a pretty standard N-Tier architecture.

Data Layer

  • Using Entity Framework 4 & generic repository/unit of work pattern.
  • Will be using stored procedures extensively (this is a requirement given to me by the powers that be).
  • Holds a reference to the Business Objects layer to pass data to/from the BOs.

Business Objects 'Layer'

  • Code-first POCOs.
  • The only behaviour they will have is validation stuff (name not null, quantity must be positive integer etc).
  • Does not reference any of the other layers or care about where the data is coming from.

Business Logic Layer

  • Contains my business services that will act as the middle-man between the UI and data layers and performs additional business 'rules' such as email notifications upon record changes etc.
  • Has a reference to the data layer & business objects layer.
  • Passes BOs to/from the UI.

UI Layer

  • Holds all the usual UI stuff - web forms, javascripts, stylesheets etc.
  • References the Business Logic & Business Objects layers.

Few questions...well, alot actually :/

  1. I can see the advantages of using DTOs when the application is going to sit across multiple servers, will be using web services, or when the data passed to the view is a compilation of multiple types of business objects, but is there really a need to convert all BOs to DTOs before passing them between the BLL & UI when everything will be on the same physical machine? Most of the DTOs would be a "copy, paste, tack 'DTO' to the end of the classname" affair so it seems like a really pointless waste of time? All I'd end up with is higher maintenance costs and marginally slower performance (have to update two classes with identical code now instead of one).
  2. Assuming there will be occasions where DTOs are required, is it acceptable to add validation to the DTOs (ie: name must not be null, quantity must be a positive integer etc).?
  3. Would it be best/better practise to seperate the validation code into it's own class library using something like FluentValidation? If I did this the BOs would basically become DTOs because they'd have zero behaviour..in which case it seems I would end up with an anaemic domain model?
  4. Is it acceptable for the data layer to reference the business objects layer when the BOs are used by EF?
  5. Seeing as my service "Find( predicate )" and "FindAll( ) " methods would be using the same stored procedure (that pulls ALL records), does this mean that doing something like:

    List<Foo> myFoo = FooService.Find( f => f.Country == "Australia" && f.Status = OrderStatus.New );
    

would actually pull ALL records via the stored procedure, with the filtering performed after the fact? In which case how can I use a stored procedure that only retrieves the needed records, while at the same time allowing for the use of linq expressions (needs to allow for &&, ||, != etc)? Is this something that's even possible (without having to ditch sprocs)?


Solution

  • I think it's an over-architecture if it will be a monolithic application. Following YAGNI and DRY, just make your services return the BOs. I also recommend using MVP pattern when working with WebForms and even avoid the "service layer" and just do Domain Driven Design (DDD) instead.

    http://en.wikipedia.org/wiki/Domain-driven_design

    http://en.wikipedia.org/wiki/Don't_repeat_yourself

    http://en.wikipedia.org/wiki/You_ain't_gonna_need_it

    http://www.codeproject.com/Articles/23562/Building-an-MVP-Framework-for-NET-Part-1-The-Basic

    MVP is really easy to implement. The explanations will be always full of components and arrows, and the sample implementations always try to make things complex (I don't know why), but is really a simple pattern for separation of concerns and it really works :)

    Hope it helps :)