Анемичные модели предметной области или “куда девать логику”

StackOverflow https://stackoverflow.com/questions/1425749

Вопрос

Это один из тех сценариев, когда, похоже, наступает "Паралич с помощью анализа", поэтому, пожалуйста, дайте совет!

Проект

Довольно простой список автомобильных товаров, который включает в себя такие детали, как ссылка на детали, к каким автомобилям они подходят и т.д.

Интерфейс - это приложение на основе asp.net MVC.

Серверной частью является SQL, использующий Subsonic для проецирования продуктов в объекты домена.

Функциональность

Один из наших экранов - это экран сведений о продукте.ASP.NET Контроллер MVC вызывает репозиторий продукта для извлечения сведений о продукте, возвращает эти сведения (посредством некоторого автоматического сопоставления с ViewModel) в представление.

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

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

например ,0900876, если смотреть через Торговый канал, становится 1700876.

Где я изо всех сил пытаюсь решить, где инкапсулировать "правила канала" в отношении ссылок на детали (и других деталей, которые могут измениться).

Я рассмотрел эти альтернативы.

Запишите логику непосредственно в объект домена

На Продукт класс у нас мог бы быть метод / свойство для получения ссылки на переведенную часть.

public string TranslatedPartRef()
    {
        if (this.Channel == "Trade")
        {
            return PartRef.Replace("0900", "1700");
        }
        else
        {
            return PartRef;
        }
    }      

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

Инкапсулировать логику в другой объект

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

Однако чего я не понимаю, так это того, как затем координировать эти два класса.

Если контроллер вызывает репозиторий для извлечения Продукта, должен ли он затем определить, какой канал был использован, и перевести ссылку на деталь?если да, то как мне затем отправить продукт с переведенной ссылкой на деталь обратно в представление?

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

Это было полезно?

Решение

Я не специалист по C #, но, я думаю, я бы занялся этим с помощью Декоратора на Java.

Предполагая, что у вас есть интерфейс для продукта, вы можете создать Декоратор, который управляет выдачей номера детали.

class Product implements IProduct {
    public String getProductCode();
    // etc
}

class ProductChannelDecorator implements IProduct
{
    // constructor, like this in C#?
    public ProductChannelDecorator(IProduct product, Channel channel) { 
        this.product = product;
        this.channel = channel;
    }
    public String getProductCode() {
        switch (this.channel) {
            case Channel.RETAIL:
                return this.decorated.getProductCode();
            case Channel.TRADE:
                return retailToTradeTransformer(this.product.getProductCode());
            // etc
        }
    }
    // etc
}

Другие советы

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

Дополнительный вопрос, который вы могли бы задать, это: если в будущем мне понадобится создать другое приложение поверх этой модели предметной области (напримервеб-сервис или богатый клиент), нужно ли мне все еще иметь дело с концепцией канала?

Я предполагаю, что ответом может быть ДА.

Насколько я понимаю ваш вопрос, Канал каким-то образом связан с контекстом запроса.Возможно, это действительно атрибут пользователя.Или, возможно, in - это атрибут самой конфигурации приложения.

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

Если нет, то реализация декоратора, предложенная ptomli, звучит как хороший подход.

Сколько было бы различных вариантов номера детали.Если бы это была просто Торговля v Розничная торговля, у меня был бы большой соблазн просто иметь оба числа в объекте Product, и пусть пользовательский интерфейс решает, какое из них отображать.При действии на продукт идентификатором может быть "тип {Торговля, Розничная торговля}, номер".

Для чего-то гибкого в режиме, я думаю, что идея вашего канала подойдет.Но если бы у него были двунаправленные обязанности, связанные с привязкой розничной торговли к торговле и из нее, это, казалось бы, сработало.Объект Channel можно рассматривать как адаптер, способный к другим преобразованиям и обогащениям.

В качестве реализации я бы создал отдельный объект Channel для каждого канала, пытаясь избежать операторов case и логики if else else.Для розничной торговли объект Channel может быть объектом NOOP, для торговли он может выполнять сопоставления.Фабрика может создать объект approporaye Channel.

Что, если отображение номера детали может измениться?Прямо сейчас это префикс, который меняется, но могут ли быть другие типы изменений, которые вы должны учитывать?Может быть, тебе это и не нужно, но:

На бизнес-уровне вы говорите, что продукт может иметь разные номера деталей в зависимости от канала (что, в конце концов, является фундаментальной бизнес-концепцией).Таким образом, это наводит на мысль, что на уровне базы данных где-то может существовать таблица PartNumber, содержащая столбцы ProductID, channelId и PartNumber.Это, безусловно, касается случая, когда со временем появляется больше каналов (сегодня это розничная торговля, завтра они могут добавить интернет, заказ по почте и т.д.все из которых, предположительно, могли бы хотеть разные номера деталей).

На уровне объекта это сопоставляется с Product экземпляр , имеющий Dictionary<Channel, PartNumber> который может быть использован для получения соответствующего номера детали с учетом Channel.

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

Прямое решение:

public interface IChannel
    function GetNumber(Part as IPart) as String
end interface

Никаких декораторов, никаких переключателей, никакой инверсии управления.

Каждый раз, когда вам нужен номер детали для определенного канала, вы вызываете этот метод.

dim Channel as IChannel = ...
dim Part as IPart = ...
dim PartNumber = Channel.GetNumber(Part)

Каждый раз, когда вам нужен другой метод расчета номера детали, вы просто реализуете этот интерфейс.

public class TradeChannel
    implements IChannel

    public function GetNumber(Part as IPart) as String implements IChannel.GetNumber
        return Part.Number.Replace("0900", "1700")
    end function
end class
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top