2

I'm trying to write some business logic for my app. At the core of my app are EF generated entities from Database First model. I have separated generated classes (.tt file ) from .edmx part.

I want to find the best place where to put my business logic, but the problem is, that business logic requires some complicated dependencies, for example, needs logging, calls some WebService, or makes pure SQL calls to DB. Because of that, I can't just use new() inside functions and create hard dependencies, but I want to somehow abstract away from them, following DI principles.

public class Person
{
    public Person(IDbCaller dbCaller, IWebServiceCaller webServiceCaller) { }
}

My first bet was to use partial classes, that extend EF classes.

But after reading some articles, I am now thinking that injecting dependencies into EF classes is not a good idea:

Why not use an IoC container to resolve dependencies for entities/business objects?

http://lostechies.com/jimmybogard/2010/04/14/injecting-services-into-entities/

So, where should I put this logic? I agree, that dependencies on EF entities are bad, but I cant really find solutions. Logic needs to be somewhere. I see a few options/questions:

1) Put business logic (that requires dependencies) inside Service layers. This could lead to Anemic Domain Model, but maybe Service layers are the right place for this kind of logic, that requires dependencies?

2) Create some king or Wrapper/Factory classes, that I need to call every time query returns entities, so I can wrap entity with business logic.

3) Put that logic inside some other classes, that take entity as a function parameter.

What are some good, common practices for this?

Community
  • 1
  • 1
andree
  • 3,084
  • 9
  • 34
  • 42

1 Answers1

8

The problem is that you are mixing two opposite designs: DDD and anemic model.

  • DDD: as far as the business logic there can be no dependencies, because the objects themselves are what must implement it
  • anemic model: this is the usual way to work with EF. The objects are POCOs: Fowler says "bags of properties setters and getters", and consider it an antipattern. All the logic is implemented in services that receive the entities, and change them according to business rules

If you use DDD

The classes in your application must be domain objects (entities and value objects). You must implement them without thinking how they will be stored in the database (or whatever backend you use). When you finish the domain object design you implement an EF model which satisfies the persistency needs of your domain objects. So, in many cases there will be not direct mapping between the domain objects (entities and value objects) and an EF entity.

I.e. in this implementation your domain object will have to store data at some point. And you'll have to implement an EF model that supports storing this data.

As to the dependencies, in this case they'll be in your domain objects, not in your EF entities. Then you can use the usual patterns (Factory, DI) to create your domain models. Probably one of this dependencies will be Repositories implemented with EF.

If you use anemic model

First I must say that there are many succesful implementations of this kind, even if Fowler says it's an anti-pattern. I'm not going to agree or disagree with Fowler, but I must expose the facts.

If you implement your application using an anemic model, you can start by designing your EF (anemic) model, and then implement all the business logic in service classes.

The big mistake

An anemic model is usually the result of a data-centric design. In the words of Fowler: I don't know why this anti-pattern is so common. I suspect it's due to many people who haven't really worked with a proper domain model, particularly if they come from a data background. So, if you start by doing this: At the core of my app are EF generated entities from Database First model. you'll have trouble to implement pure DDD (you can read this article to understand why)

Answering your comments:

1) this is an anemic model

2) you're expecting a direct mapping from stored data to domain objects. You receive an entity and want to add functionality to it. Nope! Your domain object has to use EF to request the persisted data, and not the other way round!

3) this is 1) again

You can find this article very interesting: The Anaemic Domain Model is no Anti-Pattern, it’s a SOLID design And its refutal: Rich Domain Is SOLID , Anaemic Domain Is An Anti Pattern

JotaBe
  • 38,030
  • 8
  • 98
  • 117
  • I'm sorry, but breaking EF dependency is not what I'm talking about. I want to abstract away depencencies, that EF POCOs/Domain classes have, to perform business logic. – andree Jun 06 '14 at 04:15
  • I've understood your question after this comment. Your question was very confusing because you were mixing up concepts. I've updated my answer. – JotaBe Jun 06 '14 at 09:25
  • +1 with a few mentions: value objects are domain objects too, the anaemic domain aka the procedural way works when you just move data around but fails when your app is full of contextual business rules which change often (it's not like it doesn't work, but it's a pain to implement changes fast without breaking things). Anaemic domain has a maintainability problem. About that article, here's [my rebuttal](http://www.sapiensworks.com/blog/post/2014/03/26/Rich-Domain-Is-SOLID-Anaemic-Domain-Is-An-Anti-Pattern.aspx) – MikeSW Jun 06 '14 at 14:55
  • @andree When you're starting with the database then it's a database driven design and not domain driven. Maybe [this article](http://www.sapiensworks.com/blog/post/2014/01/13/Do-Either-DDD-or-DDD-But-Not-DDD.aspx) will help you understand why starting with db is wrong. – MikeSW Jun 06 '14 at 14:58
  • @MikeSW I've added references to your interesting articles: very interesting blog. Thanks for your comment! – JotaBe Jun 06 '14 at 23:23
  • What if the business objects need to perform some processing, and then depending on conditions do something which requires a dependency? Should the command executer (domain level) execute the method on the class and then check the results and execute the dependency code if appropriate? What if that state is private? – Peter Morris May 01 '15 at 08:27