Хранилища и сервисы, модель MVC - PullRequest
1 голос
/ 26 января 2011

Итак, я изучал модель репозитория, и кажется, что ожидается, что репозитории не выполняют много сложной логики.Однако я также читал, что большая часть бизнес-логики не должна быть внутри моего Controllers.Итак, где я могу это поставить?

Я рассмотрел некоторые примеры приложений, и кажется, что у них есть еще один слой с именем Services, который делает более сложную логику для вещей.Итак, как этот фактор входит в модель MVC?

Хочу ли я создавать свои службы для доступа к моим репозиториям, а затем к моим контроллерам для доступа к моим службам?Как это?

interface IMembershipService
{
 bool ValidateUser(string username, string password);
 MembershipCreateStatus Create(string username, string password);
}
interface IMembershipRepository
{
 MembershipCreateStatus Create(string username, string password);
}

class MembershipRepository : IMembershipRepository
{
 public MembershipRepository(ISession session)
 {
  **// this is where I am confused...** 
 }
}
class MembershipService : IMembershipService
{
 private readonly IMembershipRepository membershipRepository;
 public MembershipService(IMembershipRepository membershipRepository)
 {
  this.membershipRepository = membershipRepository;
 }

 public bool ValidateUser(string username, string password)
 {
   // validation logic
 }
 public MembershipCreateStatus Create(string username, string password)
 {
  return membershipRepository.Create(username, password);
 }
}

class MembershipController : Controller
{
 private readonly IMembershipService membershipService;

 public MembershipController(IMembershipService membershipService)
 {
  this.membershipService = membershipService
 }
}

Отмеченная часть моего кода - это то, что меня смущает.Все, что я прочитал, говорит, что я должен вводить свой ISession в свои репозитории.Это означает, что я не мог вводить ISession в свои сервисы, так как же мне получить доступ к базе данных из моих сервисов?Я не понимаю, что такое соответствующий процесс здесь.

Когда я вставил ValidateUser в свой IMembershipRepository, мне сказали, что это «плохо».Но IMembershipRepository - это место доступа к базе данных.Это намерение, верно?Чтобы доступ к базе данных был минимальным?Но если я не могу вставить в них другую логику, то какой в ​​этом смысл?

Может ли кто-то пролить свет на это и показать мне пример, который может быть более жизнеспособным?

Я использую Fluent nHibernate, ASP.NET MVC 3.0 и Castle.Windsor.

Должен ли я вместо этого сделать что-то вроде ...

class MembershipService
{
 private readonly IMembershipRepository membershipRepository;

 public MembershipService(ISession session)
 {
  membershipRepository = new MembershipRepository(session);
 }
}

И никогда не даватьКонтроллеры прямого доступа к Repositories?

Ответы [ 2 ]

4 голосов
/ 26 января 2011

Все, что я прочитал, говорит, что я должен вводить свою ISession в свои репозитории.

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

Это означает, что я не могу внедрить ISession в мои службы, так как же мне получить доступ к базе данных измои услуги?

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

Итак, резюмируем:

  • Хранилище содержит простые операции CRUD в вашей модели.Здесь осуществляется доступ к данным.Этот доступ к данным не обязательно означает базу данных.Это будет зависеть от используемого вами хранилища.Например, вы можете вызывать некоторые удаленные службы в облаке для выполнения доступа к данным.
  • Служба использует один или несколько хранилищ для реализации бизнес-операции.Эта бизнес-операция может зависеть от одной или нескольких CRUD-операций в репозиториях.Служба даже не должна знать о существовании базы данных.
  • Контроллер использует службу для вызова бизнес-операции.
  • Чтобы уменьшить связь между различными уровнями, интерфейсыиспользуется для абстрагирования операций.
1 голос
/ 26 января 2011
interface IMembershipService
{
 bool ValidateUser(string username, string password);
 MembershipCreateStatus Create(string username, string password);
}

Создание такой службы как анти-шаблон.

Сколько обязанностей у такой службы? Сколько причин может измениться?

Кроме того, если вы поместите свою логику в сервисы, вы получите анемичный домен. В итоге вы получите процедурный код в стиле сценария транзакции. И я не говорю, что это обязательно плохо.

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

Это должен быть ОГРОМНЫЙ красный флаг:

public MembershipCreateStatus Create(string username, string password)
{
  return membershipRepository.Create(username, password);
}

Какой смысл? Слои ради слоев? Сервис не добавляет здесь никакой ценности, не имеет смысла.

Пропущено множество понятий.

Сначала рассмотрим использование Фабрики для создания объектов:

public interface IMembershipFactory {
    MembershipCreateStatus Create(string username, string password);
}

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

Во-вторых, Репозитории - это абстракция коллекции объектов. Как только вы использовали фабрику для создания объекта, добавьте его в коллекцию объектов.

var result = _membershipFactory.Create("user", "pw");
if (result.Failed); // do stuff
_membershipRepository.Add(status.NewMembership);  // assumes your status includes the newly created object

Наконец, класс MyEntityService, содержащий метод для каждой операции, которая может быть выполнена над сущностью, кажется мне ужасно оскорбительным.

Вместо этого я стараюсь быть более явным и лучше понимать намерения, моделируя каждую операцию не как метод для отдельного класса Service, а как отдельные классы Command.

public class ChangePasswordCommand {
    public Guid MembershipId { get; set; }
    public string CurrentPassword { get; set; }
    public string NewPassword { get; set; }
}

Тогда что-то должно действительно делать что-то при отправке этой команды, поэтому мы используем обработчики:

public interface IHandle<TMessageType> {
    void Execute(TMessageType message);
}

public class ChangePasswordCommandHandler : IHandle<ChangePasswordCommand> {

    public ChangePasswordCommandHandler(
         IMembershipRepository repo
      ) 
      {}

    public void Execute(ChangePasswordCommand command) {
      var membership = repo.Get(command.MembershipId);
      membership.ChangePassword(command.NewPassword);
    }
}

Команды отправляются с использованием простого класса, который взаимодействует с нашим контейнером IoC.

Это помогает избежать монолитных классов обслуживания и значительно упрощает структуру и расположение логики проекта.

...