3

This probably seems as an example of opinion-based question, but I'm actually looking for rationale on how to decide correctly, I believe there is a correct solution that can be backed by solid arguments (that's why I chose SO and hope is really not only opinion based, but I can be wrong; if you'd feel so, please leave a note in a comment). I can't decode how far to go with SoC in DDD and whether is ok to have mostly empty domain objects.

We have .NET, DDD, mainly use EF as our ORM in repositories, our application lives in Azure. For complex queries, we use stored procedures that return multisets (because of Azure SQL) - so one query = one round trip to the server (is's worth it, the query is performed usually on > 10 separate tables, no joins, so it would require many round trips).

For simplicity, imagine domain object called NavigationItem, that have multiple titles (in different languages), items are stored in one table, localized titles are stored in another table, where foreign key is the item title contains culture id.

The navigation object might look like this:

public class NavigationItem
{
    public int FileRecordId { get; set; }

    public int? ParentId { get; set; }

...

    public Dictionary<int, string> Title { get; set; }
}

So the domain object, returned by repository, is constructed (in repository) this way:

// load list of navigation items
var reader = command.ExecuteReader();
navigation.Items = ((IObjectContextAdapter)_context).ObjectContext.Translate<NavigationItem>(reader).ToList();

// process localized titles
reader.NextResult();
var titles = ((IObjectContextAdapter)_context).ObjectContext.Translate<TitleInfo>(reader).ToList();

// associate titles, so the whole NavigationItem is constructed
foreach (var item in navigation.Items)
{
    item.Title = titles.Where(t => t.FileRecordId == item.FileRecordId).ToDictionary(key => key.CultureId, value => value.Title);
}

This is simple example, just to get the point - my question is, wouldn't it be better to return just list of navigation items (where I should omit the Title property) and list of titles and combine that in a service layer, when creating DTD object? Repository is responsible for persistence and retrieving persisted data, and I'm not sure, whether this is simply "too much". On the other hand, the requirement on repository might be just "give me whole entity with titles". Separation of concerns is naturally a good way, but I'm not sure if this is still concern of the repository.

Please take this just as a simple example, my question is more general - in this example, each domain object NavigationItem contains Title, so it feels it's OK to do this in repository. But when we have more complex etities, there is another problem - e.g., each File has common properties, like Name. Some files have image properties, same have also pdf document properties, some have localized titles... some don't. In this case, it seems much more "natural" to have one entity (common File), and return List, List, List, ... from repository and combine it in the service. Or on the other hand, do all the translation in repository, but it would mean that domain object (aggregate root) File will contain entities like ImageInfo, TitleInfo, etc., which will be in most cases null.

What approach - and mainly, why - would you recommend?

EDIT: To be more specific - from DDD perspective, is it ok keep Entity partially empty and sometimes in repository just retrieve few properties (or sometimes all of them), or is better approach to separate such entity to smaller ones and return one entity that aggregates lists of all possible entities? I believe that there can be reasonable answer, typically "yes, partly initialized entity is ok", or "no, it's wrong and you should always use as granular entities as possible", etc.

migrated from stackoverflow.com Sep 23 '14 at 11:53

This question came from our site for professional and enthusiast programmers.

  • Ignoring all the technology specifics, it sounds like you want to use a polymorphic class design (different classes derived from File) but your tools don't allow that. So the question I would ask, why are you using those tools? Granularity should be based on the object model design foremost, ignoring the tool restrictions. – Frank Hileman Jan 15 '15 at 19:26
1

Sounds like you are using domain entities for both domain operations (commands, which modify application state) and queries (which don't modify application state, but return data for the user). The problems you describe are a direct result of trying to get that single domain entity to do both jobs, which don't mix very well.

My recommendation is to read on CQRS (Command Query Responsibility Segragation) e.g. CQRS by Martin Fowler and try to limit your domain entities to only retrieve what data they need to complete the domain operation. Even though the amount of classes and interfaces will grow, your application will become much more simple and more maintainable due to more adherence to the Single Responsibility Principle.

Also, your choice of domain objects and especially Aggregate roots leads me to question the validity of your domain model. Is a File really such an entity, that it's of dire meaning to the business people? What kind of operations do you do on the file, and what's their value to the business? The whole idea in Domain Driven Design is to identify the actual business entities on which you should act, as if you wouldn't have a computer at all. An Order is a common example. Operations on that Aggragate root would be "Submit", "Cancel" etc. File is just an infrastructural concern, which most often has no value or place in the domain logic.

  • Thanks for the answer, the problem I described was only about queries, we use different entities for domain operations. CQRS doesn't suite our application and frankly I don't think it mix well with what we use, ASP.NET MVC, we heavily use DDD with domain events and it so far worked out quite nicely. Our business logic is about files and folders, operations are e.g. create folder, update file, etc., so file as an aggregate is right, I believe. – Robert Goldwein Jul 3 '15 at 13:04
  • 1. If you have separated the queries and the domain operations, you are already using CQRS. That's all CQRS is: Separating the read operations (queries) into other objects than the write operations (domain operations). 2. If your question is in regards to queries, then the term "domain object" is misplaced, as domain objects are commonly used indeed on the command side of the application, not on the query side. This took me off course on the answer. To answer the original question more correctly, could you provide an example in your domain of the kind of query that comes into question here? – Tuukka Haapaniemi Jul 6 '15 at 19:20
  • @RobertGoldwein: Forgot to add a notify for you, so here it is now separately. Sorry for the extra comment. – Tuukka Haapaniemi Jul 7 '15 at 19:21
  • Thanks, to be more specific - this is a matter of DB layer, repository returns domain objects. An example of such domain object would be a file - internally, in DB, it's stored in many tables, repository recombines it and returns domain object, all layers deal with "proper" domain objects. The problem is better described in the "EDIT:" section of my question - ordinary file has only data. Image file has information about width and height, PDF file about pages, etc. The domain object could contain all properties (those would be just for specific files and otherwise empty), or do it differently? – Robert Goldwein Jul 9 '15 at 10:34
  • From what Fowler says in the article about CQRS, it's a risky approach: "... so far the majority of cases I've run into have not been so good, with CQRS seen as a significant force for getting a software system into serious difficulties." I agree with Fowler when he says "CQRS should only be used on specific portions of a system and not the system as a whole". – Rogério Jul 13 '15 at 19:46

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.