2015-11-27 6 views
1

У меня есть слоистое решение следующим образом:Какая опция, отличная от BLL, создающая DAL, допускает модульное тестирование в n-уровневом решении, не подвергая DAL UI или BLL DAL?

  • UI (пользовательский интерфейс)
  • BLL (Business Logic Layer)
  • ВВЛ (уровень доступа к данным)
  • SharedEntities (A VS Project с лицом Pocos только)

Я бы хотел, чтобы BLL получил услугу GetProductList(), которая реализована в моем слое DAL. Я думал об определении интерфейса в реализации BLL и DAL следующим образом:

Вариант А:

// Interface defined in BLL 
public interface IDataServices 
{ 
    List<Product> GetProductList(); 
} 

// Interface implemented in DAL 
public class DataServices : IDataServices 
{ 
    Public List<Product> GetProductList() 
    { 
     return //do some database work here and return List<Product>; 
    } 
} 

Если я хочу, чтобы это было реализовано в DAL, то я должен был бы иметь проект DAL ссылаться на BLL, чтобы увидеть определение интерфейса IDataServices. Или я мог бы реплицировать определение интерфейса в DAL, но затем я получу дублированный код для поддержки (то же определение интерфейса в BLL и DAL).

Вариант B: Другой способ, которым я мог бы сделать это забыть идею интерфейса и просто сделать следующий конкретный класс и вызов метода в УСК, который пользовательский интерфейс может использовать:

// Concrete class defined in the BLL 
public class DataServices 
{ 
    Public List<Product> GetProductList() 
    { 
     DAL aDAL = new DAL(); 
     Return (aDAL.GetProductList()); 
    } 
} 

Это достаточно легко но тогда BLL видит DAL и имеет ссылку на него, но это действительно плохо? Пока BLL не использует какие-либо объекты базы данных (например, источники данных, соединяют строки и т. Д.), Чтобы удовлетворить запрос, а DAL соответствует сопоставлению имен служб, которые я определяю в классе BLL DataServices, недостаточно? Все разговоры, которые я слышу о замене в другом движке базы данных, все равно можно сделать, просто удостоверившись, что следующий DAL предоставляет те же сервисы, которые BLL идентифицирует в классе DataServices, например GetProductList(). В этой настройке пользовательский интерфейс все еще ничего не знает о DAL, и DAL ничего не знает о BLL. Если я принимаю идею использования инъекции зависимостей, чтобы избежать создания экземпляра DAL в BLL, это означало бы, что он должен был бы внедрить его в пользовательский интерфейс, который будет передан в BLL. Я бы не хотел этого делать, чтобы предоставить доступ к интерфейсу DAL.

Вариант C: Я кратко рассмотрел контейнер Unity, но этот инструмент предложил зарегистрировать все интерфейсы и конкретный класс вверх в точке входа, который был бы пользовательским интерфейсом, который, в свою очередь, обеспечивал видимость интерфейса для обоих BLL и DAL, которые выглядели еще хуже. Я видел ссылку на использование MEF с Unity, чтобы обойти проблему точки входа, видя все слои, но также увидел, что если вы это сделаете, вы не сможете полностью протестировать такую ​​конфигурацию. Похоже, что много работы и сложности по сравнению с опцией B.

Опция D: И еще один вариант, о котором я думал, - создать слой фасада (другой проект VS) между BLL и DAL. Это, похоже, не имеет большого смысла, если я не получаю много методов DAL, которые не касались BLL, поэтому они должны были быть скрыты; позволяя DAL Facade показывать только то, что требуется BLL. Если бы мне пришлось поменять местами в другой базе данных, мне все равно пришлось бы создавать методы, которые необходимо было использовать на основе потребностей BLL, как я упоминал в варианте B.

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

  • 1) Не позволяйте UI увидеть DAL
  • 2) Не позволяйте DAL увидеть BLL
  • 3) Решение по-прежнему позволяет всем подлежащих тестированию на единицу измерения

ответ

1

IDataServices должно быть определено в DAL. but then the BLL sees the DAL and has a reference to it это естественный способ сделать это. Проект может ссылаться на слой под ним. Если вы не разрешаете ссылаться вниз, вообще не может быть ссылок.

Обратите внимание, что ссылка Reflection остается ссылкой, потому что вы не можете изменить слой ниже, не меняя слой выше. Зависимости не являются единственной концепцией. Вариант B не добавляет ничего хорошего. Он удаляет зависимую от compiletime, но не удаляет зависимость от времени выполнения.

Точка удаления зависимости от A до B заключается в том, что вы можете изменить B без изменения A. Это целая точка. Зависимости времени выполнения все еще подсчитываются.

Относительно C: вы можете настроить пользовательский интерфейс BLL для регистрации его зависимостей. Затем BLL может запросить регистрацию DAL. Таким образом, пользовательский интерфейс защищен от DAL.

D: Я не знаю, что это выполнит.

Ваши ограничения могут быть легко выполнены путем определения DAL IDataServices.

+0

BLL (или модель домена) - это самый низкий уровень. Он не должен зависеть от (ссылки) каких-либо других слоев. Бизнес-логика, реализованная в BLL, достаточно сложна, чтобы избежать каких-либо технических сложностей, возникающих на уровне инфраструктуры (DAL и т. Д.). –

+0

@JakubLortz - странная модель. Обычно BLL находится между UI и DAL. Почему DAL (в вашей модели) ссылается на BLL? Почему UI ссылается на DAL? Это то, что вы говорите. – usr

+0

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

2

Ваш вариант A (интерфейс в BLL, реализация в DAL) + и контейнер IoC - лучший подход.

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

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

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

В первую очередь сосредоточиться на твердых частях. Бизнес-логика будет сложной. Бизнес-логика не будет зависеть от выбранного вами решения для хранения, поэтому не зависеть от проекта DAL в вашей системе. Определите интерфейсы для репозиториев ваших объектов (агрегаты, если вы следуете DDD) на уровне бизнес-логики.

Вы должны быть в состоянии проверить бизнес-логику тороидально. Вы не можете этого сделать, если BLL зависит от DAL.

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

Чтобы отделить пользовательский интерфейс от BLL, используйте отдельные классы ViewModel и Command, чтобы отображать и принимать информацию от пользователя. Используйте дополнительный уровень приложения для организации BLL в каждом прецеденте.

Такой подход хорошо известен как Hexagonal architecture, Onion architecture или Clean architecture. Он также упоминается в книгах по дизайну домена Driven Design.

Конечно, вам понадобится место, где вы ставите все эти зависимости вместе. Это место является composition root и должно быть как можно ближе к точке входа в приложение. Если вы не хотите ссылаться на все слои в проекте пользовательского интерфейса, переместите корень композиции в другой проект.

+0

Я пробовал подход с корневым составом в главной() точке входа для приложения WinForms, но это дало доступ к пользовательскому интерфейсу все. Я не пытался создать отдельный проект, чтобы поместить его, поэтому я думаю, что я дам эту попытку и получим UI-вызов этого корневого проекта композиции при запуске. Одна из проблем заключается в том, что у меня есть класс рабочих классов, управляющий общим репозиторием, который настроен на поддержку двух объектов. Единица рабочего класса реализует IDisposable, поэтому, если она удаляется, то ее экземпляр недействителен. Это была одна вещь, которую я должен исследовать для контейнера, с которым нужно иметь дело. Много работы. – Robertcode

+0

@Robertcode Переместите пользовательский интерфейс (все формы и т. Д.) В другой проект. Сделайте метод 'Main' своим корнем композиции.Тогда ваш корень - отдельный проект - это единственное место, которое ссылается на все другие проекты. Другие проекты строго следуют принципу гексагональной/луковой/четкой архитектуры, не зависящей от них. –

+0

BLL может быть в зависимости от DAL абстрактным способом. Это не будет зависеть от конкретного DAL. По этой причине он также может быть протестирован, и я ввожу макет DAL. Можете ли вы привести некоторые причины, по которым BLL не может даже ссылаться на DAL абстрактным способом (например, через интерфейсы)? Я думаю, что это разумный выбор, но я не знаю, почему так можно было бы сделать так. – usr