1

First, as I know, Entity in DDD is almost same with Value Object except Entity has identity. Every article I have read say same thing that entity id has ORM mapping with any ORM tool. But I don’t want to use ORM mapping in Entity. Instead, I would like to do database operation with Repository Interfaces without mapping. And, in this case, I am stuck on how I should do this.

I will explain in my mind with an example below

Let’s assume I have a TODO application and there are some questions in the TODO and some answers in each those questions.

We have 3 value object (or entity): Todo TodoQuestion TodoQuestionValue

Now, I thought that I have a value object (or entity) for TODO. This value object has a method to get questions that gets array of TodoQuestion value object. And inside of TodoQuestion value object we have a method to get values of questions that gets array of TodoQuestionValue.

<?php
class Todo{
   private int $id;
   /**
    * @param array<TodoQuestion> $questions
    */
   private array $questions;
   private TodoRepositoryInterface $repository;
   public function __constructor(TodoRepositoryInterface $repository){
      $this->repository = $repository;
   }
   public function getQuestions(){
      $this->questions = $this->repository->listQuestionsByTodoId($this->id);
   }
}
<?php
class TodoQuestion{
   private int $id;
   private string $question;
   /**
    * @param array<TodoQuestionValue> $values
    */
   private array $values;
   private TodoRepositoryInterface $repository;
   public function __constructor(TodoRepositoryInterface $repository){
      $this->repository = $repository;
   }
   public function getValues(){
      $this->values = $this->repository->listValuesByQuestionId($this->id);
   }
}

Now, I would like to get your opinions about how I could shape this structure by following the DDD rules.

Thank you.

Furkan
  • 415
  • 5
  • 17

1 Answers1

2

Let’s assume I have a TODO application and there are some questions in the TODO and some answers in each those questions.

You just need to translate this into code. The only entity here is the Todo. Make it the aggregate root and create a repository for it.

<?php

class Todo
{
    private int $id;
    /**
     * @param array<TodoQuestion> $questions
     */
    private array $questions;

    public function getQuestions()
    {
        return $this->questions;
    }
}

The question has answers. You can model that with 2 value objects: Question and Answer.

<?php

class TodoQuestion
{
    private string $question;
    private array $values;
    /**
     * @var TodoAnswer[]
     */
    private array $answers;

    public function getValues()
    {
        return $this->values;
    }

    public function getAnswers(): array
    {
        return $this->answers;
    }
}

Your model should never depend on the repository. The repository task is to save the state of your aggregate and rebuild the same state when you query your aggregate.

interface TodoRepository {
    public function save(Todo $todo): void;

    public function todoOfId(TodoId $id): Todo;
}

You don't need more than this 2 methods. If you want to have the list of questions for one Todo you just get the Todo aggregate root and then call ->getQuestions(). In your repository implementation you can decide to use the orm, write raw queries, serialize the aggregate and then save it... The possibilities are endless just make sure you keep your model decoupled from these infrastructure concerns.

Mohamed Bouallegue
  • 1,334
  • 15
  • 19
  • thank you very much for your effort and answer. For clarification, I want to ask something. I assume that the no stupid questions principle applies here as well :) Where would I call that TodoRepository from? Normally I had "service classes" which the repository was injected to. Those repositories returned primitive values rather than value objects. Should I still wrap the todo repository in there? What would the best method to initialise the todoQuestion in this case? Should it be done in the Aggregate root? Vielen Dank – Furkan Mar 05 '20 at 11:32
  • 1
    Repositories should only return an aggregate root or a collection of aggregate roots. You can call the repository from domain services or application service. To add questions to a Todo you first get the Todo object the repository, create a new question object and add it $todo->addquestion($question). Then you can save the aggregate using the repository – Mohamed Bouallegue Mar 05 '20 at 13:16