2009-08-12 2 views
7

Я работаю с унаследованной системой, которая имеет анонимную модель домена .Рефакторинг доменной логики, которая обращается к репозиториям в старой системе

В данном домене зарегистрированы следующие категории: Car, CarType, CarComponent, CarComponentType.

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

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

Первоначально я реализовал это в классе обслуживания.

public boolean canBeDiscontinued(CarComponentType carComponentType) { 
    List<Car> cars = carRepository.getCarsWithComponent(carComponentType); 
    return cars.isEmpty(); 
} 

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

public boolean canBeDiscontinued() { 
    List<Car> cars = carRepository.getCarsWithComponent(this); 
    return cars.isEmpty(); 
} 

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

Уместно ли на самом деле иметь этот метод в классе обслуживания, как я это делал вначале? Разве это не важно? Есть ли другая альтернатива? Должен ли я начинать рефакторинг с другой отправной точки?

Существует аналогичный вопрос here. Но мой вопрос связан с Java, поэтому я не думаю, что это решение применимо в моем случае. Также, извините заранее за использование автомобилей и компонентов в качестве моей модели домена. :)

ответ

3

Это звучит как хороший кандидат на Specification pattern

+0

В каком слое я бы поставил реализацию спецификации? Инфраструктура? – Chris

+0

Спецификация является неотъемлемой частью уровня домена, так как в спецификации реализованы «концепции домена». –

5

Frederik Gheysels ответ хорош, хотя, возможно, немного меньше. Разработать: В вашем случае, вы могли бы начать с defininig интерфейс для спецификации (простите мой C# синтаксис):

public interface ICarSpecification 
{ 
    bool IsSatisfiedBy(CarComponentType carComponentType); 
} 

Вы можете создать implmenetation из ICarSpecification, который использует ваш репозиторий. Что-то вроде этого:

public class CanDiscontinueSpecification : ICarSpecification 
{ 
    private readonly CarRepository carRepository; 

    public CanDiscontinueSpecification(CarRepository carRepository) 
    { 
     this.carRepository = carRepository; 
    } 

    public bool IsSatisfiedBy(CarComponentType carComponentType) 
    { 
     return this.carRepository.GetCarsWithComponent(carComponentType).Any(); 
    } 
} 

Вы можете остановить прямо там, но то, что я не особенно нравится шаблон спецификации является то, что она не очень обнаруживаемым.Одним из возможных решений было бы вводить в спецификации самого CarComponentType:

public class CarComponentType 
{ 
    private readonly ICarSpecification discontinueSpec; 

    public CarComponentType(ICarSpecification discontinueSpec) 
    { 
     this.discontinueSpec = discontinueSpec; 
    } 

    public bool CanBeDiscontinued() 
    { 
     return this.discontinueSpec.IsSatisfiedBy(this); 
    } 
} 

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

public bool CanBeDiscontinued(ICarSpecification spec) 
{ 
    return spec.IsSatisfiedBy(this); 
} 

Такой метод на самом деле не добавляет никакого значения с точки зрения реализации, но более узнаваем.

+0

Спасибо за разъяснение. Если бы у меня было больше спецификаций, которые могли бы быть объединены, это кажется уместным. В моем случае я не уверен. Вместо того, чтобы вводить репозиторий в каждый объект, я добавляю спецификацию, которая * использует * репозиторий. Это действительно намного лучше? – waxwing

+2

Ну, он отделяет ваш класс от репозитория, и, вероятно, проще провести тестирование, передав в реализациях спецификаций, которые делают то, что вы хотите для теста. –

+0

Да: какие только что сказали крики :) Серьезно, это делает спецификацию только другой Стратегией, которая может или не может зависеть от Репозитория. Тем не менее, я включил альтернативу Injection Method, если вы действительно не любите иметь универсальную зависимость от Спецификации. –

1

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

Однако, учитывая, что система, над которой вы работаете, уже установила модель модели/услуги (анти) модели анемичного домена, я думаю, что введение дополнительных шаблонов может быть связано с отклонением системы, когда она выполняется в отдельных случаях, поскольку, я считаю, вы здесь , Такое решение должно быть принято командой и должно иметь явные преимущества.

Кроме того, IMHO, я думаю, что ваши исходные фрагменты кода проще, чем предлагаемые решения в других ответах (без обид Марк и Фредерик, просто наблюдение :-) и что более сложное решение на самом деле не приносит никаких преимуществ - Я имею в виду, что в обоих случаях функциональность одинакова, но в дальнейшем используется больше движущихся частей.

С точки зрения того, как действовать, с одним примером трудно сказать. Представление ORM (которое вы упомянули в настоящее время не используется) может быть одним из способов, так как это должно уменьшить код в ваших классах услуг и иметь явные преимущества, поэтому у меня возникнет соблазн начать работу, а затем, когда это будет сделано ситуация.

+0

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

3

Я не думаю, что «это может быть прекращено» относится к любому из этих классов. Кто несет ответственность за определение того, может ли часть быть прекращена? Не Автомобиль или Автомобиль. Я думаю, что вы были на правильном пути с вашим первоначальным методом, реализованным в сервисе. Возможно, вам нужно CarInventoryManagementService, что отвечает за ответы на вопросы о товарно-материальных ценностей автомобиля, таких как:

carsUsingComponent(CarComponent comp) 
canComponentBeDiscontinued(CarComponent comp) 
etc 

Если у вас есть несколько мест в коде, которые нужно задать инвентарные вопросы, связанные, например, ваш «canBeDiscontinued», то это может иметь смысл для создания службы именно с этой ответственностью.

+0

Это немного субъективно. Чаще всего это не пограничный случай, должен ли объект домена «знать», как он используется клиентами. Возможно, вы правы в этом случае. Но, допустим, это другая функция - то, что, несомненно, вписывается в CarComponentType, но нуждается в данных не содержится в самом объекте. Это то, чем я больше интересуюсь. – waxwing

+0

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

Смежные вопросы