Модели анемичных доменов или «где поставить логику» - PullRequest
4 голосов
/ 15 сентября 2009

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

Проект

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

Внешний интерфейс - это приложение MVC asp.net.

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

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

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

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

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

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

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

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

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

В классе Product у нас может быть метод / свойство для получения переведенной ссылки на деталь.

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

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

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

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

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

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

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

Ответы [ 5 ]

2 голосов
/ 15 сентября 2009

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

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

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

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

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

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

2 голосов
/ 15 сентября 2009

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

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

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
}
0 голосов
/ 13 октября 2015

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

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

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
0 голосов
/ 15 сентября 2009

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

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

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

0 голосов
/ 15 сентября 2009

Сколько будет различных вариантов номера детали. Если бы это было просто Trade v Retail, я бы очень хотел, чтобы в объекте Product были просто оба числа, и пользовательский интерфейс решал, какие из них отображать. При действии на товар идентификатором может быть «type {Trade, Retail}, number».

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

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

...