Циклическая ссылка между сервисами с использованием модели домена Anemic

Я работаю над проектом со сложным бизнесом. Рассмотрим два класса: AccountService и SchoolService.

Я использую Unity и преобразователь зависимостей веб-API для реализации внедрения зависимостей в конструкторе.

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

Не могли бы вы представить, как это решить?

Вот пример:

public class SchoolBLC : ISchoolBLC
{
    public School GetSchool(int schoolId)
    {
        ...
    }

    public bool RenewRegistration(int accountId)
    {
        bool result = true;

        IAccountBLC accountBLC = new AccountBLC();
        // check some properties related to the account to decide if the account can be renewed
        // ex : the account should not be 5 years old
        // check the account created date and do renewal

        return result;
    }
}

public class AccountBLC : IAccountBLC
{
    public void ResetAccount(int accountId)
    {
        ISchoolBLC schoolBLC = new SchoolBLC();
        School accountSchool = schoolBLC

        // get the school related to the account to send a notification 
        // and tell the school that the user has reset his account
        // reset account and call the school notification service
    }

    public Account GetAccount(int accountId)
    {
        ...
    }
}

Эти два класса ссылаются друг на друга, это ситуация для 70% BLC в проекте.


person Jek    schedule 17.10.2016    source источник
comment
Можете ли вы привести пример такой циклической зависимости в вашем проекте.   -  person user1849310    schedule 17.10.2016
comment
Похоже на плохой дизайн ... Обычные вещи я бы перенес на третью службу. Это решит круговую зависимость. Механизмы DI обычно генерируют исключение для циклической ссылки.   -  person SledgeHammer    schedule 17.10.2016
comment
@SledgeHammer Ближе к делу. Как бы вы решили проблему даже без DI? DI не волшебный, если вы не можете сделать это без него, вы не можете сделать это с ним.   -  person Aron    schedule 18.10.2016
comment
Я хотел бы добавить к ответу SledgeHammer. Ваша проблема в том, что у вас есть два тесно связанных класса. Если они связаны на 70%, не означает ли это, что это одна и та же единица кода? Рассмотрим основной принцип ООП - инкапсуляцию; вы должны предоставлять как можно меньше функциональных возможностей. Когда у вас есть два класса с методами, которые вызываются только друг другом, ВЫ НЕ ВЫПОЛНЯЕТЕ КАПСУЛЯЦИЮ. Это означает, что ваш код не является ООП.   -  person Aron    schedule 18.10.2016
comment
@SledgeHammer, не могли бы вы объяснить, как я могу исправить дизайн и что должна содержать 3-я служба.   -  person Jek    schedule 18.10.2016
comment
@ Арон. Нет. Я предполагаю, что если интерфейс A и B имеет много общего кода / функциональности, он должен быть либо в базовом классе, либо разбит на интерфейс C. Затем A и B используют интерфейс C вместо B и A, и вы нарушаете циркулярная ссылка.   -  person SledgeHammer    schedule 18.10.2016


Ответы (2)


Если вам абсолютно необходимо сделать это таким образом, у вас может быть интерфейс, который выполняет вашу логику IoC и разрешает это в реализации, которая обертывает разрешение Unity, например

public interface ITypeResolver
{
    T Resolve<T>();
}

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

Таким образом, когда обе службы инициализированы, они не будут напрямую зависеть от другой службы, а только от ITypeResolver

person KMoussa    schedule 17.10.2016

Я сделаю так, как предлагает @KMoussa, но с некоторыми изменениями:

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

public class SDPContext : ISDPContext
{
    private ITypeResolver _typeResolver;

    public Account CurrentUser { get; set; }

    public IAccountService AccountService
    {
        get
        {
            // lazy load the account service
        }
    }

    public ISchoolService SchoolService
    {
        get
        {
            // lazy load the schoolservice
        }
    }

    public SDPContext(ITypeResolver typeResolver)
    {
        this._typeResolver = typeResolver;
    }
}

public class ServiceBase
{
    public ISDPContext CurrentContext { get; set; }

    public ServiceBase(ISDPContext context)
    {
        this.CurrentContext = context;
    }
}

public class AccountService : ServiceBase, IAccountService
{
    public AccountService(ISDPContext context) : base(context)
    {

    }

    public bool ResetAccount(int accountId)
    {
        // use base.Context.SchoolService to access the school business
    }
}

public class SchoolService : ServiceBase, ISchoolService
{
    public SchoolService(ISDPContext context) : base(context)
    {
        //this._accountService = accountService;
    }

    public void RenewRegistration(int accountId)
    {
        // use the base.Context.Account service to access the account service
    }
}
person Jek    schedule 19.10.2016