Архитектура DDD для проекта ASP.NET MVC2 - PullRequest
3 голосов
/ 24 октября 2010

Я пытаюсь использовать Domain Driven Development (DDD) для моего нового проекта ASP.NET MVC2 с Entity Framework 4. После некоторых исследований я придумал следующие соглашения о слоях для каждого уровня в своем собственном проекте класса:

MyCompany.Domain

     public class User
    {
        //Contains all the properties for the user entity
    }

    public interface IRepository<T> where T : class
    {
        IQueryable<T> GetQuery();
        IQueryable<T> GetAll();
        IQueryable<T> Find(Func<T, bool> condition);
        T Single(Func<T, bool> condition);
        T First(Func<T, bool> condition);
        T GetByID(int id);
        void Delete(T entity);
        void Add(T entity);
        void Attach(T entity);
        void SaveChanges();
    }

  public interface IUserRepository: IRepository<User> {}

    public class UserService
    {
        private IUserRepository _userRepository;
        public UserService(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }
    // This class will hold all the methods related to the User entity
    }

MyCompany.Repositories

public class UserRepository : IRepository<User>
{
    // Repository interface implementations
}

MyCompany.Web --> Это проект MVC2

В настоящее время мой уровень Repositories содержит ссылку на уровень Domain.Насколько я понимаю, внедрение UserRepository в класс UserService очень хорошо работает с модульным тестированием, поскольку мы можем передать поддельные пользовательские репозитории.Таким образом, с этой архитектурой, похоже, мой веб-проект должен иметь ссылки как на мой домен, так и на уровни репозитория.Но так ли это?Потому что исторически уровень представления имел только ссылку на уровень бизнес-логики.

Ответы [ 3 ]

10 голосов
/ 25 октября 2010

Несколько замечаний по поводу ответа @ RPM1984 ...

Однако в вашем вопросе у вас есть IRepository в доменном проекте, я бы поставил это в вашей сборке репозиториев.

Хотя неважно, куда Вы помещаете вещи физически, важно помнить, что абстракции репозиториев являются частью домена.

У нас также есть сервисный уровень, который является посредником между пользовательским интерфейсом.(Контроллеры) и репозиторий.Это позволяет централизованно размещать логику, не принадлежащую репозиторию, - такие как Paging and Validation.

Я считаю, что не стоит создавать сервис только для подкачки и проверки.Еще хуже, если он создан искусственно - просто потому, что «все проходит через сервисы» и «таким образом, вам не нужно ссылаться на репозитории из пользовательского интерфейса».По возможности следует избегать служб приложений.

Для проверки входных данных - уже есть хороший момент для этого "из коробки".Если вы используете asp.net mvc, подумайте о том, чтобы следующий шаблон MVVM и инфраструктура предоставили достаточно хороших способов для проверки ваших моделей входного представления.

Если вам нужно взаимодействие между несколькими агрегатными корнями (это признак того, что вы можете отсутствовать)новый агрегатный корень) - напишите домен сервис.

Вы, похоже, на правильном пути, но, похоже, хотите пойти "DDD-All-The-Way"Рассматривали ли вы возможность применения шаблона «Единица работы» для управления несколькими репозиториями, использующими один и тот же контекст?

Я думаю, что также следует избегать единицы работы. Вот почему :

Еще один пример, UoW - это действительно проблема инфраструктуры, зачем вы вносите это в домен ?.Если у вас правильные совокупные границы, вам не нужна единица работы.


Репозитории не принадлежат домену.Репозитории - это постоянство (то есть инфраструктура), домены - это бизнес.

Хранилища получили смешанную ответственность.С одной стороны - бизнесу все равно, как и где будут храниться данные.Из других - знание того, что покупатели могут быть найдены по содержанию их корзины покупок (упрощенный пример).Следовательно - я утверждаю, что только абстракций должны быть частью домена.Кроме того - я против прямого использования репозиториев из доменной модели, потому что оно должно быть невежественным.

Сервисный уровень обеспечивает центральную точку для «проверки бизнеса».

Прикладные услуги в основном это просто фасадов .И каждый фасад плох, если сложность добавляет сложностей, которые он решает.

Вот плохой фасад:

public int incrementInteger(int val){
  return val++;
}

Службы приложений не должны содержать бизнес-правила.В этом и заключается смысл доменного проектирования - вездесущий язык и изолированный код, который отражает бизнес максимально просто и просто.

MVC хорош для простой проверки, но не для сложных бизнес-правил (представьте шаблон спецификации).

И вот для чего это нужно.Например, чтобы проверить, может ли опубликованное значение быть проанализировано как datetime, которое должно быть передано в качестве аргумента в домен.Я называю это проверкой пользовательского интерфейса.Есть многих из них .

RE UoW - я заинтригован этим предложением, но не могли бы вы уточнить?UoW - это действительно проблема, связанная с инфраструктурой, но, опять же, это в моем хранилище / уровне данных, а не в моей области.Все, что у меня есть в домене - это бизнес-объекты / спецификации.

Шаблон единицы работы побуждает нас ослаблять совокупные корневые границы.В основном - это позволяет нам выполнять транзакции по нескольким корням.Несмотря на то, что он находится за пределами домена, он все же оказывает неявное влияние на решения, принимаемые нами в области и моделировании.Довольно часто это приводит к так называемой модели анемичной области.

Еще одна вещь: слоев! = Уровни .


Еще одно замечание, которое я хотел бы отметить, это то, что DDD - это руководство, а не «все-и-все-все-конец».

Да, но это не должно служить оправданием.:)

Другая причина для нашего уровня обслуживания заключается в том, что у нас есть веб-API.Мы НЕ хотим, чтобы наш веб-API вызывал в наши репозитории, нам нужен хороший свободный интерфейс, через который могут обращаться как веб-приложение, так и API.

Если нет ничего более, чем получить root и вызвать его метод- Сервис просто обернуть, что не нужно.Поэтому - я подозреваю, что ваша настоящая проблема заключается в отсутствии этой изоляции, следовательно - необходимо организовать взаимодействие между корнями.

Здесь Вы можете увидеть некоторые детали моего текущего подхода.

Еще одна БОЛЬШАЯ причина для нашего уровня обслуживания - наши репозитории возвращают IQueryable, поэтому они не имеют никакой логики.Наш Сервисный Уровень проецирует выражения linq в бетоны

Это не звучит правильно.Та же проблема - отсутствие изоляции.

В этом случае - хранилища недостаточно абстрактны для сохранения.У них должна быть логика - та, которая связана с настойчивостью.Знание, как хранить и извлекать данные.Делегирование того, что «где-то снаружи» разрушает всю точку хранилищ и все, что останется - точку расширения для проверки доступа к данным для тестирования (что неплохо).

Другое дело, если хранилища возвращают необработанныеIQueryable, который автоматически связывает уровень обслуживания с неизвестным (!) Провайдером LINQ.

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

Не забывайте, что речь идет о технических проблемах.Эти вещи не имеют ничего общего с design , они просто делают это возможным.


Если вы не используете сервисный уровень, как бы вы (например,) восстановить список заказов на товар?Вам понадобится метод в вашем репозитории под названием «GetOrdersForProduct», который я не считаю хорошим дизайном.Ваш интерфейс репозитория становится огромным, его невозможно поддерживать.Мы используем очень общий репозиторий (Найти, Добавить, Удалить и т. Д.).Эти методы отрабатывают объект, установленный в нашей модели.Универсальность / мощность запросов передаются на уровень обслуживания

Этот хитрый.Я просто оставлю хорошая ссылка .

С неизвестным провайдером, я имею в виду - Вы действительно не можете сейчас узнать, что находится под уровнем обслуживания.Это может быть Linq для объектов, это может быть Linq для xml, Linq для sql, NHIbernate.Linq и множество других реализаций провайдера.Плохая вещь - вы не можете знать, что поддерживается.

Это будет прекрасно работать, если это Linq для объектов, и потерпит неудачу, если его нужно преобразовать в sql:

customers.Where(c=>{var pickItUp=c.IsWhatever; return pickItUp;});
8 голосов
/ 24 октября 2010

Это очень правильно, и на самом деле очень похоже на настройку, которую мы имеем.

Однако в вашем вопросе у вас есть IRepository в доменном проекте, я бы поместил это в вашу сборку репозиториев.

Ваш слой Домен должен содержать сущности вашего домена и бизнес-логику. Ваш слой Репозиторий должен иметь общий интерфейс репозитория и конкретные его реализации, по одной для каждого совокупного корня.

У нас также есть слой Service , являющийся посредником между пользовательским интерфейсом (контроллерами) и репозиторием. Это позволяет централизованно размещать логику, не принадлежащую репозиторию - такие как пейджинг и проверка.

Таким образом, пользовательский интерфейс не ссылается на хранилище, а только на уровень обслуживания.

Мы также используем DI для внедрения EntityFrameworkRepository в наш сервисный уровень и MockRepository в наш тестовый проект.

Похоже, вы на правильном пути, но, поскольку он, похоже, хочет пойти по пути "DDD-All-The-Way", рассматривали ли вы возможность применения шаблона Unit of Work для управления совместным использованием нескольких репозиториев тот же контекст?

0 голосов
/ 26 октября 2010

Возможно, вы захотите исследовать, как платформа Sharp Architecture решает эту проблему, так как похоже, что вы в основном воплощаете те же идеи. В руководстве по , посвященном *1003*, есть хорошее обсуждение этих концепций.

...