Проблема
При разработке софта, используя многослойный дизайн, легко можно смешать слои особенно это удается новичкам, в итоге получается однослойная трудночитаемая лапша, а не многослойный дизайн.
Например, наше приложение состоит из 3х слоев:
- Бизнес-логика (Domain)
- Сущности (Entity)
- Репозитории (Repository)
- Презентеры (Presenters)
- Пользовательский интерфейс (UI)
- Разрешить использовать репозитории только в слое презенторов (возлагаем ответственность сохраняемости бизнес логики на слой презенторов)
- Запретить использование сущностей в слое UI (что бы избежать бизнес логики в EventHandler'ах)
Решение
Как одно из решений это проверять кто вызывает соответствующий код используя StackTrace класс. По стеку мы можем слегкостью определить из какого слоя был осуществлен вызов. Что я и сделал:
public static class Constraints { [DebuggerStepThrough] [Conditional("DEBUG"), Conditional("TEST")] public static void CheckRepositoryCaller() { LayerChecker.Create() .AllowAssembly("TestApp.Presenters") .Check(); } [DebuggerStepThrough] [Conditional("DEBUG"), Conditional("TEST")] public static void CheckEntityCaller() { LayerChecker.Create() .DenyAssembly("TestApp.UI") .Check(); } }Теперь осталось везде, где требуется повызвать эти методы. Например:
public class AggregateRoot { public void DoSomething() { Constraints.CheckEntityCaller(); } }Работающий пример здесь http://code.google.com/p/design-layer-checker/downloads/list.
Эти вызовы можно сделать и не явно, используя прехватчики, например это решение. Также данная проверка будет ТОЛЬКО для DEBUG и TEST версий, хотя это все можно настроить... В случае вызова метода из не правильного слоя бросается исключение BadDesignException, с подробным описанием проблемы (например: DesignInspection.BadDesignException: You can not call method 'GetAll' from type 'TestApp.Domain.Repositories.AggregateRootRepository' in layer 'TestApp.UI'). Буду благодарен за критику, идеи и размышления.
P.S.: Зачем мне это понадобилось? Я использую MVP в своих проектах. В одном из проектов из модели была вынесена ответственность по сохранению сущностей в слой презетеров. Как результат - упрощение юнит тестов, не нужно мокать репозитории, ненужна БД для тестирования модели. Но в силу того, что исползуется Linq To SQL, репозитории расположены в модели, а если есть возможность заюзать репозитории в модели, то рано или поздо это будет осуществлено. Также в некоторых проектах вся модель доступна в слое UI, то есть в обработчике событий пользовательского интерфейса легко можно наваять кусок бизнес-логики. Вот собственно это и послужило толчком к размышлениям как это можно предотвратить.
P.S. #2: Несколько другое решение, но нацеленно именно на решение той же проблемы: Как проверить приложение на соответствие архитектуре слоев
Комментарии
Отправить комментарий