Хорошо, я попытаюсь пойти коротко и прямо к сути.Я пытаюсь разработать слабосвязанное многоуровневое сервисное приложение, которое можно тестировать и поддерживать внедрение зависимостей.Вот что у меня есть:
На уровне службы у меня есть метод StartSession, который принимает некоторые ключевые данные, необходимые для начала сеанса.Мой класс обслуживания является фасадом и делегируется экземпляру интерфейса ISessionManager, который внедряется в конструктор класса обслуживания.
Я использую шаблон Repository на уровне доступа к данным.Итак, у меня есть ISessionRepository, с которым будут работать мои доменные объекты, и который я реализую, используя технологию доступа к данным du jour.В ISessionRepository есть методы для GetById, Add и Update.
Поскольку мой класс обслуживания - это просто фасад, я думаю, можно с уверенностью сказать, что моя реализация ISessionManager является реальным классом обслуживания в моей архитектуре.Этот класс координирует операции с моим доменом / бизнес-объектом Session.И вот тут возникает проблема с игрой и проблемой оболочки.
В моем классе SessionManager (конкретный ISessionManager) вот как я реализовал StartSession:
public ISession StartSession(object sessionStartInfo)
{
var session = Session.GetSession(sessionStartInfo);
if (session == null)
session = Session.NewSession(sessionStartInfo);
return session;
}
У меня три проблемы с этим кодом:
- Во-первых, очевидно, что я мог бы переместить эту логику в метод StartSession в моем классе Session, но я думаю, что это противоречило бы цели класса SessionManager, который затем просто становится вторым фасадом (или это все ещесчитать координатором?).Увы, игра в оболочку.
- Во-вторых, SessionManager тесно связан с классом Session.Я подумал о создании ISessionFactory / SessionFactory, который можно было бы внедрить в SessionManager, но тогда у меня была бы такая же тесная связь внутри фабрики.Но, может, все в порядке?
- Наконец, мне кажется, что истинные DI и фабричные методы не смешиваются.В конце концов, мы хотим избежать «нового» экземпляра объекта и позволить контейнеру возвращать его нам.И истинный DI говорит, что мы не должны ссылаться на контейнер напрямую.Итак, как же мне получить конкретный класс ISessionRepository, вставленный в мой объект домена Session?Нужно ли вводить его в фабричный класс, а затем вручную передавать его в Session при создании нового экземпляра (используя «new»)?
Имейте в виду, что это также только одна операция, и мне также нужно выполнять другие задачи, такие как сохранение сеанса, вывод списка сеансов на основе различных критериев плюс работа с другими объектами домена в моем решении.Кроме того, объект Session также инкапсулирует бизнес-логику для авторизации, проверки и т. Д., Поэтому (я думаю) он должен быть там.
Ключ к тому, чего я хочу достичь, не только функциональный, но и тестируемый.Я использую DI для разрушения зависимостей, чтобы мы могли легко выполнять модульные тесты, используя макеты, а также дать нам возможность вносить изменения в конкретные реализации, не требуя изменений во многих областях.
Можете ли вы помочь мне обернуть головувокруг лучших практик для такого дизайна и как я могу наилучшим образом достичь своих целей для твердого решения SOA, DDD и TDD?
ОБНОВЛЕНИЕ
Меня попросили предоставитьнекоторый дополнительный код, настолько краткий, насколько это возможно:
[ServiceContract()]
public class SessionService : ISessionService
{
public SessionService(ISessionManager manager) { Manager = manager; }
public ISessionManager Manager { get; private set; }
[OperationContract()]
public SessionContract StartSession(SessionCriteriaContract criteria)
{
var session = Manager.StartSession(Mapper.Map<SessionCriteria>(criteria));
return Mapper.Map<SessionContract>(session);
}
}
public class SessionManager : ISessionManager
{
public SessionManager() { }
public ISession StartSession(SessionCriteria criteria)
{
var session = Session.GetSession(criteria);
if (session == null)
session = Session.NewSession(criteria);
return session;
}
}
public class Session : ISession
{
public Session(ISessionRepository repository, IValidator<ISession> validator)
{
Repository = repository;
Validator = validator;
}
// ISession Properties
public static ISession GetSession(SessionCriteria criteria)
{
return Repository.FindOne(criteria);
}
public static ISession NewSession(SessionCriteria criteria)
{
var session = ????;
// Set properties based on criteria object
return session;
}
public Boolean Save()
{
if (!Validator.IsValid(this))
return false;
return Repository.Save(this);
}
}
И, очевидно, есть интерфейс ISessionRepository и конкретный класс XyzSessionRepository, который, я думаю, не нужно показывать.
2-е ОБНОВЛЕНИЕ
Я добавил зависимость IValidator в объект домена Session, чтобы проиллюстрировать, что используются другие компоненты.