Я пытаюсь получить концептуальное подтверждение, когда я пишу модуль (скажем, форум для целей этого обсуждения), который следует рекомендациям по проектированию, управляемому доменом, и будет иметь подключаемый репозиторий, а весь модуль будет подключаться либо локально, либо в веб-сервер (локальный хостинг dll) или через службы WCF.
Следуя рекомендациям по управлению доменами, у меня будет набор бизнес-объектов, который будет написан так:
public class Forum
{
readonly IRepository _forumRepository;
public Forum(IRepository forumRepository)
{
_forumRepository = forumRepository;
}
public string Name { get; set; }
public void Save()
{
_forumRepository.SaveForum(this);
}
}
Допустим, веб-приложению необходимо создать новый форум.
Это все хорошо, если модуль размещен локально через файл dll. Код веб-приложения просто использует Ninject (или любой другой DI) для создания экземпляра бизнес-объекта и вызова метода Save.
Однако что, если разработчики захотят ввести промежуточный уровень обслуживания? Допустим, они хотят ввести слой служб WCF между приложением и уровнем базы данных, потому что они хотят, чтобы физическая архитектура была веб-сервером -> Сервер приложений -> Сервер БД. Очевидно, что dll модуля Forum будет размещаться как на веб-сервере, так и на сервере приложений. Тем не менее, модуль больше не может использоваться.
Если веб-сервер создает экземпляр объекта с внедрением репозитория и пропускает его через слой службы WCF, поле _forumRepository будет потеряно, даже если я разрешу сериализовать мои бизнес-объекты.
Разработчик должен в любом случае заставить сервер приложений выбирать хранилище. Итак, с этим требованием, как разработчик может внедрить репозиторий на стороне сервера приложений после получения уже созданного объекта с веб-сервера? Есть ли способ указать WCF Services внедрить репозиторий, когда он создает экземпляр объекта в процессе десериализации?
Я прочитал статью, Как использовать Dependency Injection (Ninject) со службами WCF , и рассмотрел пример решения, но это только демонстрирует, как внедрить хранилище непосредственно в оказание услуг. Похоже, это не решает проблему, которую я здесь обсуждаю.
Вот пример того, как я могу предвидеть код, если он написан правильно. Как я уже говорил ранее, основная проблема заключается в том, что я не знаю, как я могу внедрить репозиторий в объект домена на стороне служб.
Код приложения (веб-приложение или приложение для Windows):
Forum newForum = new Forum();
//Set some properties on newForum.
IKernel kernel = new StandardKernel(); //Instantiate ninject kernel.
//Yes, I know using the using statement means that IService needs to derive from IDisposable. A local service would just have an empty implementation for IDisposable since there would be nothing to clean up and this would allow different service architectures to be plugged into the application.
using (IForumService forumService = kernel.Get<IForumService>()) //Get concrete implementation bound to IForumService via ninject.
{
//Send forum to service layer for local OR WCF processing (on an app server)
forumService.SaveForum(newForum);
}
Сервисный код (это проблема):
public class WCFForumService : IForumService
{
public void SaveForum(Forum forum)
{
//PROBLEM SCENARIO: How do I now inject the repository I want to use since I already have an instance of forum?
//Can I make WCF inject the repository when it is deserializing the forum object before passing it into this method?
forum.Save();
}
}
Я бы предпочел не заставлять разработчиков моего модуля форума создавать DTO для каждого объекта домена, если это вообще возможно. Это нарушает принцип СУХОГО. Было бы здорово, если бы я мог создавать свои доменные объекты, чтобы они были универсальными и / или достаточно расширяемыми, чтобы их можно было использовать в качестве самих DTO без особых усилий. Таким образом, я могу поставить проверку бизнес-правил на объектах домена с помощью аннотаций данных, и разработчик может использовать эти объекты в проекте Web Forms, MVC3 или Silverlight без необходимости повторной обработки всей проверки вручную.
РЕДАКТИРОВАТЬ: ВЫШЕ ЭТО ВСЁ НЕПРАВИЛЬНЫЙ ПОДХОД. РЕДАКТИРОВАНИЕ ДЛЯ ДЕМОНСТРАЦИИ БОЛЕЕ ДЕЙСТВИТЕЛЬНОГО ПОДХОДА.
public class Forum
{
public Forum(string name)
{
Name = name;
}
public string Name { get; set; }
}
public interface IForumRepository : IRepository<Forum>
{
void Add(Forum forum);
Forum this[object id] { get; set; }
}
//Client Code (Called from the WCF service hosting the domain)
public class WCFAppService
{
public void SaveForum(ForumDTO forumInfo)
{
IKernel kernel = StandardKernel();
IForumRepository repository = kernel.Get<IForumRepository>();
Forum forum = repository[forumInfo.ID];
if (forum != null)
{
repository[forumInfo.ID] = forumInfo.CopyTo(forum); //Save the Forum to the db.
}
else
{
repository.Add(ForumFactory.CreateFrom(forumInfo)); //Insert the Forum into the db.
}
}
}
Большая часть проблемы, с которой я столкнулся при попытке использовать этот прототип, заключалась в том, что я слишком много внимания уделял проблемам инфраструктуры (т. Е. DI, серверной архитектуре и т. Д.), Пытаясь изучить DDD в процессе. Из того, что я узнал о DDD, я пришел к выводу, что, пытаясь выяснить, как структурировать решение DDD, забудьте об архитектуре и технологии, пока не разберетесь в том, что DDD сделает для вас. Я делаю это замечание, потому что в моем реальном проекте DDD, над которым я сейчас работаю, DI кажется ненужным осложнением. Вот так просто DDD может сделать ваш код.