Составные классы принципа единой ответственности

У меня есть класс Entity

Public class Company
{
   public int Id {get;set;}
   public string Name {get;set;}
   .
   .
   .
   public List<Address> Addresses{get;set;}
   public List<Domain> Domains{get;set;}       
}

И менеджер должен использовать этот класс сущности

public class CompanyManager
{
   ctor
   {}
   public Company Get(int id)
   {
   }
   public List<Company> GetList()
   {
   }
   public int Add(Company company)
   {
   }
   public bool Delete(int id)
   {
   }
}

Я в замешательстве, должен ли я заполнить список адресов и список доменов в менеджере компании или я должен использовать фасадный слой для заполнения этих свойств. Моя путаница в том, что адреса и домены могут иметь классы менеджера. Кроме того, я не уверен, является ли это хорошей практикой или нет.


person Johney    schedule 16.04.2014    source источник
comment
Я думаю, вы запутались в том, что такое сущность на самом деле: сущность — это класс в вашей модели предметной области, идентичность которого не меняется независимо от того, сколько его свойств изменяется (кроме идентификатора, который неизменяем). Например, если я изменю свое имя, профессию, адрес и т. д., я все еще тот же человек (и в объектно-ориентированной модели все равно будет иметь то же значение идентификатора). Класс CompanyManager не подходит под это определение — это репозиторий. Возможно, я смогу помочь вам лучше, если вы начнете с этого вопроса: какой бизнес вы пытаетесь смоделировать и каковы его правила?   -  person Josh Kodroff    schedule 17.04.2014
comment
Спасибо, Джош. Я пытаюсь разработать продукт, который будет хранить данные клиента в указанном выше формате. Меня не устраивает существующий код, и я предлагаю изменить рефакторинг. Продукт будет иметь пользовательский интерфейс, который будет извлекать данные из сервиса. Служба будет нести ответственность за предоставление ViewModels с помощью контрактов данных. Мое намерение состоит в том, чтобы держать бизнес подальше от модели представления, т.е. контракта данных. Итак, я предлагаю разные классы менеджеров для каждой сущности, а Facade создаст окончательный объект контракта данных. Итак, я в замешательстве.   -  person Johney    schedule 17.04.2014


Ответы (2)


Прежде всего, я очень сомневаюсь, что компания - это просто какой-то DTO с общедоступными сеттерами, и он определяется наборами адресов и доменов. Требуется ли их правильное определение в соответствии с концепцией компании? Вы уверены, что компании необходимо полное определение адреса или домена или достаточно только идентификатора?

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

person MikeSW    schedule 16.04.2014

Согласно вашему комментарию выше, я думаю, вы могли бы использовать некоторые общие архитектурные советы. Вот подход, который я использовал, который работал для меня. Я назову его «CQRS-lite», поскольку он не использует источник событий или строго отдельные источники данных, но он решит проблему SRP, с которой вы боретесь.

Вот как мы будем читать:

public class FooController {
    private IFooService _readService;

    public ActionResult List();
}

// runs DB queries, maps them to viewmodels
// If you're using EF, use one DB context per HTTP request
// and turn off change tracking
public interface IFooService  {
    FooList List();
}

// a viewmodel, only used by this controller 
// (and often only by one action/view)
public class FooList {
    public IEnumerable<FooListItem> Foos;
}

И вот как мы будем писать (это тот же контроллер MVC — я просто разбил код между чтением и записью, чтобы его было легче читать):

public class FooController {
    private IBus _bus;

    [HttpPost]
    public void Create(FooCreate model) { // could also return, e.g. RedirectToAction
        _bus.Send(new CreateFoo {
            Id = model.Id,
            // map the other properties
        })
    }
}

// e.g. NServiceBus in memory, or you can write your own
// it just finds the handler for the command via your 
// DI container
public interface IBus {
    void Send(params ICommand[] commands)
}

// A command tells the system to do something.
// It's basically a serializable remote procedure
// call, can be sync or async
public class CreateFoo : ICommand {
    public Guid Id;
    // etc
}

// A command handler.  DbContext should be scoped per-instance
// (not per HTTP request) or things get complicated.
// You can decorate Handle() with, e.g. _context.SaveChanges()
public class CreateFooHandler : IHandler<CreateFoo> {
    private IDbContext _context;

    public void Handle(CreateFoo message) {
        // write stuff to the DB
    }
}
person Josh Kodroff    schedule 19.04.2014