cfs
cfs

Reputation: 1324

DDD in a complex enterprise MVC application, what what is the repository's responsibility and what belongs to the domain?

I'm working on a complex enterprise application. A big part of it is sharing information and managing data in different ways.

I use a Repository pattern for persisting data, but ends up with what I regard as either an Illogical or totally anemic domain.

The core of the application is the Users (Employees in this case). I have a concrete example of my problem underneath when implementing a functionality for users to send messages to:

  1. Each other
  2. For managers to display a "Must read" message with important information

For the latter I have this (a bit simplified):

    public class Message
{
    public int Id { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }
    public string Body { get; set; }
    public Employee CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }

    public ICollection<Employee> Recipients { get; set; }

    public void Send(Message message)
    {
        //Send the message
        //This is persisting it in the database and update the table
        //that maps to the recipients
        //WHO SENDS IT, THE USER?
    }

}

public class User
{
    public int Id { get; set; }
    public String Name { get; set; }
    public Department Department { get; set; }
    public ICollection<Message> Messages { get; set; }

    public Message Recieve(Message message)
    {
        //Mark the message as recieved in DB
        return message;
    }

    public void Read (Message message)
    {
        //Mark the message as read in DB
    }

    public void Delete (Message message)
    {
        //Have to query the database to see if this user can delete
        //this message
        //If so, delete the message in DB
    }

    //THE USER ALSO THEN
    //Creates messages
    //Makes orders
    //Changes passwords and adress
    //Checks deliveries
    //Corrects delivered amounts
    //Manages an department
    //Archives documents
    //Writes new documents
    //AND A LOT MORE
}

As you see most of the operations is writing information to the DB, or having to query the DB to get information to make the right decisions. In the enterprise apps I've worked on, most operations are about either managing data, storing settings and user information or logging who did what and when in the database.

Most of this fits most natural in the repository. Should I call the repository from the classes above and ask it to save the information? I have a feeling that breaks the rules. Is deleting a message a behavior at all? If not what behavior does a data-centric domain really have?

What does the User not do? I have a hard time wrapping my head around that, i.e. a message is basicly data, so it can't do anything in real life. An order can not create itself (it is also just a piece of information), a user must make it and so on. Writing files, exchanging data via xml, services, pdf or whatever is just different ways of sharing and/or persisting the data?

I know there must be someway to explain the concept but having spent hours reading about it i have a hard time finding the real meaning of DDD, and the examples I've seen is too far away from a real problem where you have to check history or get info from the DB in some way to make the right decisions. It feels like more of a philosophical exercise than logic...

If you can read frustration between the lines, I just want to point out that It's not on anyone other than myself since I can't seem to find out how to implement the message functionality and still do it by the book.

Edit

Based on question below i see I need to add a bit more information:

I use EF Code First so my domain model and persistence model is really the same (as far as I understand) since I havent done any special mapping. A user must retrieve messages at logon to display them, mark it as read, and i he/she wants to, archive it (does not show in list of messages).

As my initial question, I have no problem putting all of the needed code in the repositories (get the messages, add a new message, change messsage, mark message as read etc) but that is also my problem, what should then go in the domain model?

The application is a MVC web app that is stateless, does DDD way of thinking belong i a stateless web app in its traditional sense at all?

I like the idea behind it though, and was thinking of doing like this.

Add a new class to serve the function of an Archive, make the message an aggregate root with the archive as a child:

    public class MessageArchive
    {
        public Message Message { get; set; }
        public User Recipient { get; set; }
        public User From { get; set; }
        private bool Read { get; set; }
        private bool Archived { get; set; }

    public void MarkMessageAsRead()
    {
        //Do things - i.e. notify sender that the message is read
        Read = true;
    }

    public void ArchiveMessage()
    {
        //Do things to archive the message, in this case, mark it as archived
        Archived = true;
    }

    }


    public class Message
{
    public int Id { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }
    public string Body { get; set; }
    public User CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public virtual ICollection<MessageArchive> ArchivedMessages { get; set; } 

    public void Send(IEnumerable<User> recipients, User from)
    {
        foreach (var recipient in recipients)
        {
            ArchivedMessages.Add(new MessageArchive
                                     {
                                         From = from, 
                                         Recipient = recipient, 
                                         Message = this,
                                     });
        }
    }

}

In create a message and send it would then be:

    public void Run()
    {
        var message = new Message();
        var recipients = new List<User>();
        //Fill it with recipients
        message.Send(recipients, currentUser);
        _unitOfWork.MessageRepository.Add(message);
        _unitOfWork.Save();

    }

Am I totally on the wrong track here, should i forget about DDD and just make it work or is this a step towards better design?

Upvotes: 4

Views: 594

Answers (2)

cfs
cfs

Reputation: 1324

I just could not leave this alone, but think after a LOT of work that I have a solution that lives up to the philosophy of DDD, so this is what I ended up with:

First a message interface:

public interface IMessage
{
    int Id { get; set; }
    string Body { get; set; }
    string Title { get; set; }
    Employee CreatedBy { get; set; }
    MessageType MessageType { get; set; }
    ICollection<MessageArchive> ArchivedMessages { get; set; }
    void Send(IEnumerable<Employee> recipients);
}

Then a MessageArchive (this could be a persistance object, but i think of it as a mailbox)

    public class MessageArchive
{
    public int Id { get; set; }
    public IMessage Message { get; set; }
    public Employee Recipient { get; set; }
    public Employee Sender { get; set; }
    private bool Read { get; set; }
    private bool Archived { get; set; }

    public void MarkMessageAsRead()
    {
        //Do things - i.e. notify sender that the message is read
        Read = true;
    }

    public void ArchiveMessage()
    {
        //Do things to archive the message, in this case just mark it as archived
        Archived = true;
    }

}

I only do the PublicMessage here (the PrivateMessage also calls a MailService - I think that the details of smtp and sending the mail belongs in a service, not in the domain):

 public class PublicMessage: IMessage
{
    public int Id { get; set; }
    public MessageType MessageType { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }
    public string Body { get; set; }
    public string Title { get; set; }
    public Employee CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public virtual ICollection<MessageArchive> ArchivedMessages { get; set; } 

    public void Send(IEnumerable<Employee> recipients)
    {
        var archive = new List<MessageArchive>();
        foreach (var recipient in recipients)
        {
            archive.Add(new MessageArchive
                                     {
                                         Sender = CreatedBy, 
                                         Recipient = recipient, 
                                         Message = this,
                                     });
        }

        ArchivedMessages = archive;
    }

}

The message is the Aggregate Root, the MessageArchive is a child. In use this is significantly more readable than setting all values through a repository, and much easier to add events and functionality later.

        var message = new PublicMessage
                          {
                              //Create the message
                          };
        var recipients = new List<Employee>() {//Create a list of recipients};

        //We send the message
        message.Send(recipients);

        //Persisting it is just adding a message to through the repository and save
        unitOfWork.MessageRepository.Add(message);
        unitOfWork.Save();

So i realize this could be done in other ways with better names, and some practicalities could be solved more elegant, but I think this is much closer to a DDD way of thinking and it works.

Please comment if you have any feedback on that. Anyway I hope this might help someone that goes through the same process as me... I've practically dreamt DDD the last days, and been really bothered about how to get into the right way of thinking...

problem now is how entity framework likes Interfaces and inheritance etc, I've already encountered some problems there. It is difficult to start DDD when you have to take into account the requirements of a ORM, but it's just about experience i guess.

Upvotes: -1

MikeSW
MikeSW

Reputation: 16348

First thing, a Domain Model is different than the Persistence Model. WHen using a OR\M you define the persistence model, not the domain. When designing the domain model, you start to model the usage needs (in lack of a meaningful wording) of the stakeholders (app owners and users) and ignore anything related to the persistence, but when you design the persistence model you care about the storage/persistence needs.

The Repository abstracts everything related to persistence. The application layer knows only about the repository interface where it sends domain objects or commands and from where it gets back domain objects or view models (in fact simplistic flattened for display representation of a model).

DDD is primarly a mindset. You start with what application needs to solve and you represent the needs, the problems and solutions in code using the most natural language (can you see where i'm aiming at?). You don't care about what or how it will be saved, the database doesn't exist. However you should take into account the platform restrictions and usage patterns i.e design the objects according to how they're used and where are used. There's no point in populationg a whole Order object, when in fact you need only an id. That Order maybe an aggregate root, but you can use interfaces so that you'll work with a lightweight version of it depending on the functionality you want.

If the domain model is anemic, it means you have a VERY TRIVIAL application which is not the case here. This means it's a good idea to refactor the domain model.

There is a bit to say and some code examples would be much better but it's hard to do that in a simple answer on the spot. I hope at least I've transmitted some ideas which will be helpful for you.

Upvotes: 2

Related Questions