2

Following the classic 3 layer architecture

  • domain Model (a list of entities live there and has no dependencies)

  • DAL layer - My Repositories lives there with DBContext implementation (Ado.net) Dal return pure entities ( reference domain model)

  • Service Layer ( Business Layer ) expose methods of repositories ( reference DAL Layer and Domain Model)

  • UI Layer(WPF) > Service Layer(BLL) > Repositories

MY question is related to the following concepts

  • Repository
  • CQRS

Reading article upon article, state that Data Repositories should return only entities.

e.g

public interface ICustomerRepository 
{

    IEnumerable<Customer> FindByText(string TextToFind);
    Customer GetByID(string ID);
    IEnumerable<Customer> GetAll();
    void Update(Customer CustomerToAdd);
    void Delete(params string[] Ids);
}

Then I ask my self.

  • What if i need to show a list of customer with some other complex inner join, union query.
  • end a lot of more other queries related to other view requirements.

(actually in our accounting application 90% of the screens are never shown data in their pure form(entities). always they are in form with other inner joins , sum amounts etc.. e.g customer : Name,AccountName(inner join from accounts), Balance (inner join from ledger), etc..

Some state that is ok to put this query in repository others say is not good because is not an entity

Where this logic goes? since repositories dose not allow me to return other than entity model. and also CustomerService in BLL Layer ( dose not allow me to return DTO in methods)

I read that CQRS comes to the stage to solve UI Queries

Ok, lets say that i follow cqrs for query side ( lets skip command as commands and updates go through repository)

Now I end up having:

  • CustomerRepository (DAL Layer)
  • CustomerService (BLL - service layer) which is just exposed repository methods and maybe some other related things
  • CustomerQueries class (BLL - service layer) which contain any complex
    query (DTO) related to customer and has direct connection to sql converting datatable to relative dto and give it to UI layer

My question is this proper way to follow?

which layer CQRS live? in (business layer which some call it service layer?) or in DAL Layer where my repositories live

Many times i found my self much easy way just to type CustomerServices. and the intellisense giving me all the related information I might want from customer, some functions return dto others return some amounts other times just some bool to check some rules which require complex sql queries)

The problem is that, since CustomerService as they say, should only be responsible to call relative CustomerRepository fetch or update only things related to repository,

where i put logic for complex queries and in which layer?

1

To cut a long story short, this is mostly a matter of self-imposing a design and then trying to make something fit. Something which is very much needed, but doesn't quite fit with the design we imposed on ourselves.

In the end, it's a matter of finding the compromise in your design that you're comfortable with.

Below, I've listed the common compromises I've encountered in companies/projects I've worked at.


A silly system

In the olden days, repositories were intended to be "entity boxes". You get the Person from the PersonRepository, you get the Car from the CarRepository, and so on. From a purely coding perspective, this is the cleanest way to organize your datastores.

But then database got significantly better at relational queries, and the "entity box" approach specifically breaks the relational nature of your entities when you're trying to receive multiple levels of entities in a single optimized query. That's a problem

And now we run into one of the silliest problems: self-imposed categorization. It's not that we can't write a query that fetches a list of people and the cars they own, it's that according to our restrictive "entity box" approach, we've actually made it impossible to properly categorize this.

This is no different from labelling all the shelves in your kitchen, only to realize that you now have another food that you didn't put on any label before, and now you refuse to put that food in your kitchen because it doesn't fit with any of your labels. It's downright ridiculous and essentially a jobsworth kind of discussion.

Sticking with repositories

Since this is purely an issue because of a self-imposed pattern, creating the query isn't difficult, and justifying how it fits in your design is a matter of argumentation. You could take the purist route:

  • We should stick to the repository pattern as intended. We get the Person from the PersonRepository, we get the Car from the CarRepository. No relational two-entity-types-in-one-query tricks.

Which is great for code organization, but you're going to run into significant performance issues for large data sets. If the performance becomes unbearable, this option becomes very hard to justify for what is essentially an arbitrary self-imposed rule.

Okay, so let's decide to create this query. Where do we put it? You could find a reason to justify why it belongs in one of the existing repositories:

  • Since it returns Person with nested Car object, I consider this a Person-based query and therefore I put it in the PersonRepository.
  • I consider the owner (i.e. the Person) as car data, so the query itself is Car-based and therefore I put it in CarRepository.

These justifications are all, well, justifiable, but you tend to run into issues where one developer thinks about the same query in a different light, and they disagree on which repository the query belongs in.

So to please these disagreeing developers, we could come up with a compromise:

  • Since this query uses both Person and Car entities, we should put this in its own PersonCarRepository.

This sounds great initially, but given many entity types and many queries which fetch several entity types, the amount of repositories is going to explode. Also, if you ever expand one query, it may need to move repository because it now includes another minor entity type. Is that really worth all the effort?

None of these solutions have really worked well, or at least it's very hard to get everyone to agree on a given approach. Maybe we should move away from repositories, at least partially?

Query objects

At a very basic level, query objects are repositories that only contain one query. Instead of naming them after the entity type (CarRepository), you name them after the query itself (GetCarsForOwnerQuery), but the implementation remains virtually the same.

This actually solves the categorization problem. We no longer deal with silly labels in which our custom query doesn't fit, and instead make a new custom label for our custom query.
It also plays very nicely with CQRS, which you mentioned in your question.

Note that you could decide to refactor all your repositories into query (and command) objects, but it's not necessary to do so. You already touched on this idea:

lets say that i follow cqrs for query side ( lets skip command as commands and updates go through repository)

I tend to take such a hybrid approach:

  • Single-entity-type queries and commands remain in their entity repository. Personally, I use a generic base repository that provides all the CRUD basics to work with EF tables. It's quick and easy, but not really customization-friendly for very unique entities.
  • Multi-entity-type queries and commands get their own query/command object.

When beginning development, you tend to rely heavily on your repositories as you're crudely making entities. But over time, as the application gets fleshed out, you tend to shift more towards query objects.

Whether you use the hybrid approach or move over to using query/command objects entirely is up to you. Using only query/command objects takes a bit more work to type it all out, but you may prefer to stick to one system.

Now I end up having:

  • CustomerRepository (DAL Layer)
  • CustomerService (BLL - service layer) which is just exposed repository methods and maybe some other related things
  • CustomerQueries class (BLL - service layer)

That's not how I would do it. These query objects replace the repositories, so they should live with (or instead of) the repositories, i.e. in the DAL.

A more appropriate approach would be

  • CustomerService (BLL)
  • CustomerRepository (DAL) - for all simple CRUD operations
  • [myQueryName]Query (DAL) - each of these classes contains a single multi-entity-type query

The CustomerService (BLL) can connect to both the repositories or the query objects, depending on what it needs. The service can in principle mix and match to its liking, though usually you tend to create query objects that precisely match a specific CustomerService method.

CQRS and query objects

which layer CQRS live? in (business layer which some call it service layer?) or in DAL Layer where my repositories live

Either, both, or neither.

The query objects in the DAL, which is a form of CQRS, are technically unrelated to any CQRS you may have going on in the Business or Domain layers. These are two separate design decisions. You can have:

  • a monolith domain service and DAL query objects/commands
  • domain queries/commands and monolith repositories
  • a monolith domain service and and monolith repositories
  • domain queries/commands and DAL query objects/commands.

These are two separate design decisions that do not depend on each other. The thought process behind either is very similar, but you're not forced to make the same decision twice.

2
  • Thank you for you time answering me. I feel more safe as you said to move the queries to DAL / Repositories.. But that means the DTO should leave from BLL as also [MyQueryName]Query (as currently now live in BLL) and move to domain Model? ( Domain model ) is accessible by all layers.(there Irepository Interfaces live and things that are common to all layers) And then, BLL will be the gateway to call DAL and call function that i will pass [myQueryName]Query . Did I understand correct? – user2160275 Jan 22 at 19:17
  • @user2160275: There are several ways to handle DTOs here. The cleanest approach is for every layer to have its own DTO - even if that mean that the business and dal DTOs are exactly the same. But there are other approaches where the only real DTO being used is a business DTO, where the domain itself just passes its domain objects around. That's a long discussion in and of itself. I'd suggest starting off with a DTO per layer by default, and only deviate from it when you find a proper reason to do so. – Flater Jan 23 at 17:05
1

This confusion is not your fault. The literature sucks.

Some state that is ok to put this query in repository others say is not good because is not an entity

"Repository", in a DDD discussion, refers back to the REPOSITORY pattern described by Eric Evans in Chapter 6 of the original domain driven design book.

Evans, in that chapter, explicitly opens the possibility of Repositories returning values rather than entities.

... usually ENTITIES, sometimes VALUE OBJECTS with complex internal structure, sometimes enumerated VALUES.

The core motivation of the REPOSITORY pattern is to create a facade, so that the complexity of reconstituting the objects you need is delegated to the plumbing, allowing your application code to express its needs in the language of the model.

Therefore...

If you have a report, perhaps based on some complex inner join, it is a perfectly reasonable application of the pattern to create a REPOSITORY that represents a collection of those reports, with methods to allow the client to request a specific report (or subset of the collection).

These reports are, of course, values -- meaning that the implementation of the report doesn't include affordances that allow you to change the report. Similarly, the report collection (ie, the repository) does not include affordances for adding or updating reports. (In effect, the reports are a view into the information managed by the domain model; you can look, but if you want to change anything you need to send messages to the domain entities).

So basically DTO's should live not in Business Layer but in Domain Model Layer?

No, I don't think that follows. DTO are, in effect, an in memory representation of a message schema, where a schema is a representation of information that can be sent across time and space. That's a communication concern, not a domain modeling concern. That sort of thing normally makes sense out at the boundaries of our application, not in the core of our domain.

Message schema typically change much more slowly than our domain dynamics, and for different reasons.

If DTO and domain models change for different reasons, do you really want to design your solution so that these two different things are in the same pile of stuff?

Design is what we do to get more of what we want than we would get by just doing it.

So you need to think about which of the "what you want"s should have priority here.

1
  • Thank you ! I feel more comfortable now as is just matter of what is more convenient method to follow. So basically DTO's should live not in Business Layer but in Domain Model Layer? (a shared dll) if I understand well – user2160275 Jan 22 at 19:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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