К основному контенту

Контроль правильности дизайна или как выявить говнокод на ранних стадиях разработки


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

Например, наше приложение состоит из 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: Несколько другое решение, но нацеленно именно на решение той же проблемы:  Как проверить приложение на соответствие архитектуре слоев

Комментарии