After you read Evans' book, you will see that DDD is a collection of concepts. The book was written over a decade ago. My opinion of it today is that it has good parts but is easily misunderstood and sometimes used inappropriately. In reality, some parts you may not need or may not be right for your application or chosen tools.
As far as EF, I don't think that the DDD concept of combining data and behavior in your entities melds well with it. EF entity models should be devoid of behavior if you want to avoid a lot of unexpected errors and performance bottlenecks. As other answers here point out, using a CQ(R)S pattern is a good way of implementing behavior externally from EF entities. Bottom line is, try to avoid implementing a "rich domain model" using methods on EF entities. You can still crunch the business knowledge into "the model", but your "model" should contain different classes for data (entities) and behavior (operations).
I also don't like the idea of repositories, but that's primarily my opinion and I'm sure others will disagree when I say they really aren't necessary when you are using an ORM like EF. Maybe a decade ago, before we had modern generics, they were more useful. These days, you can wrap a single generic interface around an EF DbContext and have a single generic repository (along with unit of work).
On the other hand, things like the ubiquitous language, aggregate roots, bounded contexts, query criteria, etc. I consider to be pearls. Keeping the application layer thin is a timelessly good practice too. It sounds like the other developer might not have applied the appropriate DDD concepts, and as someone else here already said, created more problems than they solved.
Sounds like somebody wants you to write a lot of SQL.
I'm assuming here only reading data since that is all the question specifically mentions.
In the past, I have created an AutoMapper-like component which would map a DataRow to a class by examining the class's property names with reflection, then assign matching DataTable columns of same name and similar type to the class's properties.
Then building on that only a little, you can convert an entire DataTable to a list of objects. I believe AutoMapper will do this already.
The query to populate an entire object tree usually returned multiple DataTables in the DataSet.
-- data table 0
SELECT ... FROM Client WHERE ClientId = @clientId;
-- data table 1
SELECT ... FROM ClientData WHERE ClientId = @clientId;
-- data table 2
SELECT b.*
FROM ClientData a
JOIN ClientJob b ON b.ClientDataId = a.ClientDataId
WHERE a.ClientId = @clientId;
-- etc.
Then building on that and with some up-front configuration, you can convert an entire DataSet into an object tree.
var sqlToObjectConfig = new []
{
typeof(Client),
typeof(ClientData),
typeof(ClientJob)
};
// call your converter
ConvertToObject<Client>(sqlToObjectConfig, dataSet);
// it will use the config to know the type of each DataTable (by index)
So your normal steps would be:
- Develop a query to get the data you want
- Define a configuration array for the query's conversion to objects
- Define a method to execute the query and convert
You could relatively easily query a specific branch like ClientJobs on down.
Best Answer
One of the most eye-opening events for me was learning about Color-Based Modeling. It totally transformed my approach to designing systems.
The key idea is that there are four archetypes in object-oriented design:
Those are the 4 basic elements of an O-O model. And here is how it relates to your question.
The very first archetype is the Moment-Interval. If you recall one of the guidelines for basic object-oriented programming "Objects are Nouns, Methods are Verbs" you might be tempted to represent the loan application as a method "ApplyForLoan" on the Applicant object and store all the information about the application on a single Loan object. Then you might be tempted to have a "LoanStatus" enumeration on the Loan that basically lists all the phases the loan goes through as it's being processed.
The better way is to have an Application moment object. And an Applicant role object. Roles are attached to Person/Place/Thing and participate in moments (in practice, I usually give the responsibility for creating a Moment object to a Role that I identify as the "actor". In the case of the loan application the Applicant is the "actor".
Within the book, Coad talks about the concept of Predecessors and Successors. I.e. before a RiskAssessment can be performed, there must be an Application. The Moments form a chain of events that are pertinent to the system being created. For instance the loan approval system would not care about payments on the loan so they wouldn't be mapped as part of the system. Although there might be another system that does deal with these details.
The beautiful part of this approach is that the system becomes very flexible and extensible because rather than sticking new fields and methods on a bloated "LoanCustomer" object (or worse deriving from loan customer to represent different roles the customer might play in the system), we create new roles as the needs of the system grows.
I'd HIGHLY recommend picking the book up and going through it. It's a very powerful technique despite the fact that UML is no longer in favor, the concepts are timeless.