3

In our ASP.NET MVC application, we try really hard to separate concerns to each layer (we use DDD). Nevertheless, it seems that we can't avoid having at least some business logic in DAL.

For instance, there are named objects, where business requirement is that each name must be unique (but the name isn't a key). If there is a request to create new named object, the service checks whether some object with that name exists in repository, if it doesn't, factory creates new object and repository persists it (we use EF for repositories and stored procedures for query objects).

The problem is with race conditions - if two requests come concurrently (it's unlikely, but possible), both requests want to create new object under the same name, in this case both checks come out negative and two objects with the same name would be created. So far, we handle that with constrains in SQL database, so if something like this happens, an exception is thrown in repository - but this approach is simply wrong, since such requirement is strictly business requirement and it shouldn't bubble to persistence layer; if we change such requirement, e.g. that names become case sensitive, such constrain would also have to be changed, etc.).

How to implement such scenario correctly? Thanks.

0

4 Answers 4

7

BTW Agree with @Robert Harvey.

There's a subtle but problematic shift in your question -- the original question is "how to isolate business logic" but then you switch over to isolating "business requirements".

Business requirements will permeate your system. Of necessity. During the data modelling effort, that's going to be driven by the data that the business has identified, and constraints on the data. Your data model will always reflect the business requirements. For example, the business may require that you capture First, Middle and Last names for customers. So your database will contain those fields, and there's just no way to isolate that from the business requirements.

Business logic, on the other hand, is a different beast. That is, business logic is little snippets of code like the calculation for a customer's monthly fee. Or a predicate, like whether or not a customer is eligible for some promotion. If your DAL has those calculations built into it, then you've crossed a line.

Providing a mechanism like uniqueness or concurrency is definitely going to come from a business requirement, but it's not business logic. The business doesn't care how you provide concurrency and uniqueness, as long as you do. You could say it supports business logic, but isn't specified by the business.

On the other hand, a calculation of the user's annual contribution percentage, or whatever, is code the business specifies directly, and is something you want to isolate for the sake of testing, validation, flexibility, etc.

1
  • That's very clear, thanks a lot. I didn't see the forest for the trees. Mar 14, 2014 at 21:42
7

So far, we handle that with constrains in SQL database, so if something like this happens, an exception is thrown in repository - but this approach is simply wrong, since such requirement is strictly business requirement and it shouldn't bubble to persistence layer.

You can make that argument if you like, but your relational database already has the necessary indexing and concurrency mechanisms to make it very convenient and reliable to do it there.

If the idea of detecting duplicates in a database makes you uncomfortable, then think of uniqueness as an attribute of the data. Enforcing such an attribute is a perfectly valid thing to do in a database. You wouldn't argue that the type of a data field (i.e. text, float, currency) is a business rule, would you?

5
  • Thanks, I totally agree, the reason why I feel uncomfortable is that how I see DAL, it should be completely business agnostic. Checking for uniqueness in IDs is perfectly valid, since it assures DB consistency, but my scenario isn't about DB consistency, it's about business requirements (fulfilled by DAL). Mar 14, 2014 at 19:43
  • The purpose of a database is to store information about a business domain. Were that not the case, we could simply put everything into a single table. Mar 14, 2014 at 19:47
  • I have no idea why the question was migrated here, an argument "Without a language tag or some example code, it's not going to get much attention here." applies for most SO posts with DDD tag. Mar 14, 2014 at 20:02
  • @RobertGoldwein 'it should be completely business agnostic' taken to an extreme that would require you to name all your tables and columns entirely unrelated to the business. e.g. TableA.ColumnA1, TableA.ColumnA2, TableA.ColumnA3, etc. No sensible database design should ignore the business aspect
    – user153101
    Oct 15, 2014 at 13:29
  • @RobertHarvey , sorry, hijacking with another question. Would you be able to answer this one too? I'll delete these comments in some time.
    – Krishnan
    Jan 26, 2021 at 20:05
0

I would do it like this:

public class NamedObjectRepostiory
{
    private IDataStore _dataStore; // this is just a wrapper around Entity Framework to make it more testable

    public NamedObjectRepostiory(IDataStore dataStore)
    {
        _dataStore = dataStore;
    }

    public void Save(NamedObject namedObject)
    {
        // This is basically a transaction scope/DbContext instance.  You can inject the IDataSession
        // rather than the IDataStore if you want scope to be managed by your DI
        // container, e.g. session per HTTP request, which is a common pattern
        using (var session = _dataStore.GetSession()) {
            if (session.Set<NamedObject>().Any(x => x.Name == namedObject.Name))
                throw new Exception("Cannot have 2 NamedObjects with the same name.");

            // save data...
        }
    }
}

No unique constraint needed (assuming that all writes go through the application, which they pretty much have to if you're doing DDD).

0

Actually, you do not use DDD if you have a "DAL" (Data Access Layer), since there is no such layer in DDD layered architecture. In DDD, Repositories are part of the domain layer.

Also, it's a big mistake to think that a DDD Repository would not or should not contain business (or "domain") logic. In fact, in any non-trivial business application, it's inevitable to have business logic embedded in lots of queries in those Repositories. This is perfectly fine.

A DDD Repository should attempt to make use of infrastructure components related to persistence, in order to keep the code implementing database queries as simple and clean as possible. That said, such code is isolated within the Repository implementations, so it's already kept cleanly separated from the rest of the Domain layer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.