how to implement DDD repository to handle a query

2020-07-24 00:00发布

问题:

I'm struggling to implement a requirement using DDD in a .net solution. I'll try to minimize the details:

Entities:

  • Category (UId, Name)
  • Attribute (UId, Name, AttributeValues[])
  • AttributeValue (UId, Name, ParentAttributeUId)
  • Process (UId, Name, AttributeValues[], Categories[])

Data Model:

  • Each Process can have multiple Categories (e.g. ProcessCategoryMap table)
  • Each Process can have multiple AttributeValues (e.g. ProcessAttributeValue table)
  • No relation between category, attributes, attributeValues

I have a s.p. FetchByCategoryAndAttributeValues(CategoryId, AttributeValueIds []) Returns a DataSet:

  • List of Processes matching CategoryId and the AttributeValueIds
  • List of AttributeValues available to refine the search.

How to Implement the Repository plus the method to call the SP, when multiple entities are returned, it seems to me that the object returned is a value object and it seems not fiting in the existent entities repositories ?

Any Ideas ?

Regards,

Pedro

==== Edit: 2011/03/30 02:52 PM UTC ====

I'm updating my question, to value all the comments plus to help others facing similar challenges.

@Justice:

solution: Use an ORM e.g. NHibernate plus all the justifications of using ORM

I don't recall any mention of an ORM in Eric Evans ddd book. How does the choice of a specific persistence technique matter at all ? Once you have the domain sorted out, you're free to persist it any way you wish. Offcourse i'm aware that NH can help and is powerful tool but it's not a solution per se.

@Dominic / @Justice:

Solution: Move away from Store Procedures.

First of all, for me is difficult to justify when i see good T-SQL stored procedure, I'm not talking about CRUD or simple T-SQL Queries. The SP that i mention runs a few TSQL CTE, combine metric data from different tables to calculate the weights, using temporary tables and returns objective results : List Of Processes, List of Categories, List of AttributeValues. The SP is optimized and tuned for a specific SQL database/server could be an MSFT, Oracle, etc. I don't believe that transferring those calculations to the Application side, and then relying in the ORM would help the execution time plus all back and forward queries. For me there is a big difference of doing all that in server and bringing only the filtered data. I could be wrong.

@All: I've identified the real problem (like Domenic point out with the ayende link) I'm porting a solution based in a Data Centric approach to a Domain Model approach. Let's put this away, the calculations returned by the SP, affects different Models. Due the way data are persisted in the DB, there are common calculations and calculations are bounded between the entities, the question is when moving to a DDD implementation is how to get best of both implementations. And also how to keep the DBAs jobs :)

You can create a very good Domain Model, persist the data spanning over N different tables, The question is when you are querying that data how to bring to the Model and keep the DDD in good stand.

Thank you all, I'm still looking for ideas, answers, etc :)

Regards, Pedro

回答1:

Your immediate problem is how to bring over a dataset that contains some data and and some "unrelated" data that allows that data to be filtered further in a fashion consistent with a domain model.

The reason why that is a problem is because the very concept - bring over multiple tables of derived or peripherally related data is not really consistent with domain modeling. Domain modeling is about defining the relationships and complex business logic that incorporate fundamental domain knowledge of the core application. Multiple tables that do not yield fundamentally related objects means it is probably not something that is part of the domain model to begin with.

If you don't have objects which are inherently suited for what you are trying to do it means either your model is incomplete, or in this case possibly that you are attempting to leak user interface aspects of the application into the domain model. Probably the latter.

The solution is to also include a user interface view architecture like MVP or MVC. Domain objects are about enforcing the business rules across transactions - saves and updates. Use DTOs and Presenters for example to assemble any sort of "new" or "hybrid" objects that do not represent core domain knowledge but are instead constructed to present the data to the user in some manner that the user wants.

In this case you just create a DTO that incorporates the Process and Attribute DTOs into a new object for consumption in the UI.

But there are some other possibilities:

1) There are times when you have to ask yourself if you are even using the right tool for the job. I am working on a medical application that has a very complex domain model. That is the core application - enforcing complex rules across the process of data acquisition. But once that data is acquired the business is interested in doing many different things with it. The acquisition model and the analytical model are not at all good fits, so instead of trying to make them work together I think the far better option to have the acquisition model which is DDD and then use ETL and move the data into a data warehouse and give the business a separate analytical application which is built on queries, not OOP/DDD.

2) Domain modeling is about defining models that reflect the true domain, not about relational data techniques. The objective is to manage complexity and create a model which can be both refined and evolved as the business changes. You will not find many people out there doing a lot of DDD that will even pretend that optimization is an aspect of that. To the contrary, you build a model as consistently with DDD as you possibly can. If you have to later you compromise that model only as much as you have to in order to bring unacceptable performance into an acceptable range.

There are lots and lots of things you can do with queries that are much more efficient. Of course if someone does not know the application at all they may have to trace a bunch of that stuff out and quite possibly have to more or less understand the entire application before they can understand any of it all. DDD you can have people working on something successfully when they know basically nothing about the rest of the application, but you don't have anything even close to optimal in terms of performance or round trips.

As attractive and logical as it seems to try for the best of both worlds, I do hardcore SQL stuff like ETL and data warehousing, and I do DDD. I have some doubts as to how successfully you can merge the two worlds in one application. Instead of the best of both worlds the chances are you will end up with an app no one can work with and does not perform well either. If you have a bunch of efficient stored procedures, there is bound to be a bunch of business logic in there. If you also have "objects" that have business logic then what you end up with is business logic in the database, business logic in the "object model" which turns out to be just (yet another) application that has classes, but is not OOP or DDD - pretty much the same thing people have already been doing for years and calling it "n-tier".

Don't get me wrong - DDD apps should still be built on solid database and relational principles and there is nothing wrong with performance. But a lot of db server processing is in effect domain activity leaked to the databases. Also a lot of these techniques of optimal data handling violate multiple principles of OOP and DDD.

If you are not willing to completely abandon all the database stuff, and it is working well for you, you may not need or even want to move to a DDD concept in the first place. If you want to DDD the best approach is to consider everything you have as a source of valuable domain knowledge but in terms of implementation details legacy code which is being completely abandoned. DDD is not really suitable for "porting" a none DDD app over to.



回答2:

You need to somehow abstract your persistence code. That belongs in an infrastructure layer, not in the domain layer (see DDD page 68). You can do this manually, writing code that mucks with DataSets, or using an ORM like NHibernate.

If you were to do it manually, the pattern I would recommend would be similar to that explored in the Payroll Case Study in Martin's Agile Patterns, Practices, and Principles in C#. The diagram around page 569 shows how he packages the components.



回答3:

Use NHibernate.

Let the ORM be your repository. That's what it's for.



回答4:

If you're already using Entity Framework, there are a couple of ways to do this (this answer has overlap with Pedro's), each with own limitations & use cases:

  1. Query EF entities and return DTOs.
  2. Query SQL directly using DBContext's Database.SqlQuery. If your DTO property names match your SQL columns then it maps them for you.