Как настроить Ninject с помощью служб WCF, поддерживая архитектуру доменного управления? - PullRequest
2 голосов
/ 29 октября 2011

Я пытаюсь получить концептуальное подтверждение, когда я пишу модуль (скажем, форум для целей этого обсуждения), который следует рекомендациям по проектированию, управляемому доменом, и будет иметь подключаемый репозиторий, а весь модуль будет подключаться либо локально, либо в веб-сервер (локальный хостинг 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 может сделать ваш код.

Ответы [ 2 ]

5 голосов
/ 02 ноября 2011

Передавая свои репозитории на веб-сервер, вы пропускаете слои. Ничто не мешает вам вызвать сохранение или манипулировать данными так, как это не разрешено вашими бизнес-правилами. Кроме того, вы не хотите заботиться о деталях базы данных на веб-сервере. Это ответственность сервера приложений.

Между приложением и сервером БД должен быть совершенно другой интерфейс, чем между приложением и веб-сервером. Интерфейс сервера приложений должен состоять из действий, которые вы хотите выполнить на вашем веб-сервере.

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

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

В вашем примере вы должны указать метод ChangeForumName(int forumId, string newName). Теперь вы можете убедиться, что имя должно соответствовать правилам. Например. не пустой.

4 голосов
/ 29 октября 2011

Если вы действительно хотели иметь сервисы в объекте домена после отправки его по проводам, то укажите свои сервисы в качестве свойств ваших объектов и в методе «Get» используйте какой-либо шаблон CommonServiceLocator для получения сервисов в Обратная сторона. Для получения дополнительной информации см. Здесь http://commonservicelocator.codeplex.com/ (т. Е. Вместо WCF, внедряющего службы, объект вызывает CommonServiceLocator для получения экземпляров служб, которые ему требуются).

Другим подходом может быть написание пользовательского поведения wcf, которое внедряло сервисы. Проверьте эту статью для более подробной информации - http://msdn.microsoft.com/en-us/magazine/cc163302.aspx

Однако вы должны более внимательно подумать о своей архитектуре и о том, чего вы хотите достичь. Если вы хотите, чтобы Web-сервер-> Сервер приложений -> Сервер базы данных, то на самом деле вам не следует отправлять свой объект на сервер приложений, чтобы сервер приложений мог внедрять службы, а затем вызывать метод «сохранения» для вашего объекта. Более чистым дизайном было бы иметь сервер приложений (в данном случае wcf), у которого есть служба с методом сохранения, которая принимает ваш объект «Форум» в качестве параметра. Тогда этому сервису будет добавлен репозиторий в соответствии со статьей, на которую вы ссылаетесь.

Как вы знаете, при использовании WCF вы действительно отправляете по сети только сериализованные данные, а не реальные объекты. Объекты реконструируются на другой стороне. Многие архитектуры используют объекты передачи данных для своих вызовов WCF, а затем гидратируют свои доменные модели с помощью этих DTO (хотя вы можете просто использовать свои доменные объекты, чтобы помнить, что вы просто отправляете данные).

В этой статье хорошо обсуждаются эти темы http://msdn.microsoft.com/en-us/magazine/ee236638.aspx (хотя ваш вопрос не касался DTO, этот принцип применим и к доменным объектам и может дать вам пищу для размышлений о том, как вы размещаете кусочки вместе). Также стоит посмотреть, как другие делают подобные вещи. Есть несколько отличных примеров приложений, например, http://sankarsan.wordpress.com/2009/04/12/a-layered-aspnet-mvc-application-part-i/ (вам нужно прочитать всю серию, поскольку он не говорит о wcf в первых 2 частях)

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