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.

asked Mar 14, 2014 at 19:35
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.

answered Mar 14, 2014 at 21:31
2
  • That's very clear, thanks a lot. I didn't see the forest for the trees. Commented Mar 14, 2014 at 21:42
  • Main problem I have is complex access permissions. Weather or not a user has access to something is business logic but the optimal way to handle is is if the query just checks access by joining tables and filtering results directly from access permissions stored. I guess that's your point tho right? Once permissions are stored the business logic part is over and the DAL is just responding to it. It does require the current users details however and has Logic to adjust the query based on the user context. Commented Aug 26, 2024 at 5:37
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?

answered Mar 14, 2014 at 19:38
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). Commented 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. Commented 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. Commented 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 Commented 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. Commented 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).

answered Mar 15, 2014 at 18:36
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.

answered Aug 5, 2019 at 22:06

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.