В подходе DDD, этот пример правильно смоделирован?

Просто создал acc on SO, чтобы спросить об этом:)

Предполагая этот упрощенный пример: создание веб-приложения для управления проектами...
Приложение имеет следующие требования/правила.

1) Пользователи должны иметь возможность создавать проекты, вставляя название проекта.
2) Имена проектов не могут быть пустыми.
3) Два проекта не могут иметь одно и то же имя.

Я использую 4-слойную архитектуру (Пользовательский интерфейс, Приложение, Домен, Инфраструктура).
На моем уровне приложений у меня есть следующий класс ProjectService.cs:

public class ProjectService
{
    private IProjectRepository ProjectRepo { get; set; }

    public ProjectService(IProjectRepository projectRepo)
    {
        ProjectRepo = projectRepo;
    }

    public void CreateNewProject(string name)
    {
        IList<Project> projects = ProjectRepo.GetProjectsByName(name);
        if (projects.Count > 0) throw new Exception("Project name already exists.");

        Project project = new Project(name);
        ProjectRepo.InsertProject(project);
    }
}

На моем доменном слое у меня есть класс Project.cs и интерфейс IProjectRepository.cs:

public class Project
{
    public int ProjectID { get; private set; }
    public string Name { get; private set; }

    public Project(string name)
    {
        ValidateName(name);
        Name = name;
    }

    private void ValidateName(string name)
    {
        if (name == null || name.Equals(string.Empty))
        {
            throw new Exception("Project name cannot be empty or null.");
        }
    }
}




public interface IProjectRepository
{
    void InsertProject(Project project);
    IList<Project> GetProjectsByName(string projectName);
}

На моем уровне инфраструктуры у меня есть реализация IProjectRepository, которая выполняет фактический запрос (код не имеет значения).


Мне не нравятся две вещи об этом дизайне:

1) Я читал, что интерфейсы репозитория должны быть частью домена, но реализации не следует. Это не имеет для меня никакого смысла, поскольку я считаю, что домен не должен вызывать методы репозитория (незнание персистентности), что должно отвечать требованиям служб на уровне приложения. (Что-то говорит мне, что я ужасно ошибаюсь.)

2) Процесс создания нового проекта включает в себя две проверки (не нулевые, а не повторяющиеся). В моем проекте выше эти две проверки разбросаны в двух разных местах, что усложняет (imho), чтобы увидеть, что происходит.

Итак, мой вопрос: с точки зрения DDD, это правильно смоделировано или вы сделаете это по-другому?

Ответ 1

Я думаю, что часть путаницы с (1) заключается в том, что вам не хватает слоя - вставьте сервисный уровень в свою архитектуру, и ваша проблема исчезнет, ​​как магия. Вы можете поместить службу и реализацию репозитория в уровень сервиса - то есть у вас есть служба, которая использует конкретную реализацию репозитория. Другие службы могут свободно выбирать альтернативную реализацию репозитория, если они этого захотят. Ваше приложение может свободно выбирать любой сервис-интерфейс, который ему нравится. Сказав это, я не уверен, что это действительно важно в большинстве случаев. Почти во всех моих приложениях у меня есть один "домен/datalayer", который в основном исправлен. Я могу наложить на него репозиторий или нет, в зависимости от сложности бизнес-логики. То же самое с сервисом - это может просто не понадобиться, если проект не очень сложный. Если это станет так позже, я всегда смогу рефакторировать. Обычно я помещал свой репозиторий в тот же проект, что и мой контекст данных (используя LINQ), и если бы существовал сервис, он был бы в отдельном проекте (потому что обычно он будет отображаться как веб-сервис).

Что касается (2), вам нужно подумать о проблеме с точки зрения concurrency. Ваша проверка на дублируемое имя лучше всего обрабатывается ограничением базы данных, если это возможно. Я думаю, что это самый простой способ обеспечить соблюдение этой логики. Вы можете, конечно, проверить, есть ли дубликат перед попыткой вставки, но если вы не делаете этого внутри транзакции, вы не можете гарантировать, что другой процесс не появится и вставьте его между вашим чеком и вашей вставкой. Ограничение базы данных решает эту проблему. Перемещение проверки в логику вставки (та же транзакция) также решает проблему, но независимо от того, я думаю, вам нужно быть готовым обработать ее как сбой вставки, а также (или вместо) ошибку проверки.

Ответ 2

Процесс создания нового проекта включает в себя две проверки (не нулевые, а не повторяющиеся). В моем проекте выше эти две проверки разбросаны в двух разных местах, что усложняет (imho), чтобы увидеть, что происходит.

Проект не может и не должен знать обо всех проектах в приложении (сам элемент не должен знать обо всех других элементах в списке), поэтому - ответственность службы домена (вместо службы приложения. проверьте книгу Эванса, чтобы понять точную разницу).

Существует много видов проверки. И не может быть универсального механизма валидации. DDD просто говорит, что вы должны поместить проверку домена в модель домена.