Подтвердить что ты не робот

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

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

  • Наша база кода старше десяти лет и, наконец, разбивается на швы, когда мы пытаемся масштабировать.
  • Верхние "слои", если вы хотите назвать их такими, представляют собой беспорядок классического ASP и .NET.
  • Наша база данных заполнена кучей нечестивых хранимых процедур, которые содержат тысячи строк бизнес-логики и проверки.
  • Ранее разработчики создавали "умные" решения, которые не расширялись, не подвергались повторному использованию и демонстрировали очень очевидные анти-шаблоны; они должны быть устаревшими в короткий срок.

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

(на высоком уровне)

High-level

(Слои "Бизнес и данные" )

Business and Data layers in depth

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

Здесь разбивка с описанием каждой сборки:

  • Company.Project.Common.OperationalManagement: Содержит компоненты, которые реализуют политики обработки исключений, протоколирование, счетчики производительности, конфигурацию и трассировку.
  • Company.Project.Common.Security: содержит компоненты, которые выполняют аутентификацию, авторизацию и проверку.
  • Company.Project.Common.Communication: Содержит компоненты, которые могут использоваться для связи с другими службами и приложениями (в основном, с множеством повторно используемых клиентов WCF).
  • Company.Project.Business.Interfaces: содержит интерфейсы и абстрактные классы, которые используются для взаимодействия с бизнес-уровнем с уровнями высокого уровня.
  • Company.Project.Business.Workflows: Содержит компоненты и логику, связанные с созданием и обслуживанием бизнес-процессов.
  • Company.Project.Business.Components: Содержит компоненты, которые инкапсулируют бизнес-правила и проверку.
  • Company.Project.Business.Entities: содержит объекты данных, которые являются репрезентативными для бизнес-объектов на высоком уровне. Некоторые из них могут быть уникальными, некоторые могут быть композитами, образованными из более гранулированных объектов данных из уровня данных.
  • Company.Project.Data.Interfaces: содержит интерфейсы и абстрактные классы, которые используются для взаимодействия с уровнем доступа к данным в стиле репозитория.
  • Company.Project.Data.ServiceGateways: Содержит клиентские службы и компоненты, которые используются для вызова и извлечения данных из внешних систем.
  • Company.Project.Data.Components: Содержит компоненты, которые используются для связи с базой данных.
  • Company.Project.Data.Entities: Содержит гораздо более гранулированные сущности, которые представляют бизнес-данные на низком уровне, подходящие для сохранения базы данных или другого источника данных транзакционным способом.

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

  • Являются ли мои соглашения об именах для каждого модуля и его соответствующей сборки стандартными соглашениями, или есть другой способ, которым я должен заниматься?
  • Полезно ли разбить бизнес и слои данных на несколько сборок?
  • Полезно ли иметь интерфейсы и абстрактные классы для каждого уровня в своих собственных сборках?
  • САМЫЙ ВАЖНО. Полезно ли иметь сборку "Сущности" как для бизнеса, так и для слоев данных? Меня беспокоит, что если вы включите классы, которые будут сгенерированы LINQ to SQL внутри компонентов доступа к данным, то данный объект будет представлен в трех разных местах базы кода. Очевидно, что такие инструменты, как AutoMapper, могут помочь, но я все еще не 100%. Причина, по которой я их разломала, как это, - A - Обеспечить строго-слоистую архитектуру и B - Содействовать ослаблению связи между слоями и минимизировать поломку, когда происходят изменения в бизнес-области за каждым объектом. Тем не менее, я хотел бы получить некоторые рекомендации от людей, которые гораздо более приукрашены в архитектуре, чем я.

Если бы вы могли ответить на мои вопросы или указать мне в правильном направлении, я был бы очень благодарен. Спасибо.


EDIT: Хотелось включить некоторые дополнительные детали, которые, кажется, более уместны после прочтения ответа Бабуна. Таблицы базы данных также являются нечестивым беспорядком и в лучшем случае являются квазиреляционными. Тем не менее, мне не разрешено полностью переквалифицировать базу данных и выполнять очистку данных: самая дальняя до ядра, которую я могу сделать, - это создать новые хранимые процедуры и начать осуждать старые. Поэтому я склоняюсь к тому, чтобы сущности, явно определенные в слое данных, пытались использовать классы, созданные LINQ to SQL (или любые другие ORM), поскольку объекты данных просто не кажутся выполнимыми.

4b9b3361

Ответ 1

Я на самом деле только начал то же самое, так что, надеюсь, это поможет или, по крайней мере, сгенерирует больше комментариев и даже поможет мне :)

1. Мои правила именования для каждого модуля и его соответствующей сборки соответствуют стандартным соглашениям, или я должен действовать по-другому?

Согласно MSDN Names of Namespaces, похоже, все в порядке. Они выложили это как:

<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]
For example, Microsoft.WindowsMobile.DirectX.

2. Полезно ли разбивать бизнес-уровни и уровни данных на несколько сборок?

Я определенно считаю полезным разделить бизнес-уровни и уровни данных на несколько сборок. Однако в моем решении я создал только две сборки (DataLayer и BusinessLayer). Другие детали, такие как Interfaces, Workflows и т.д., Для которых я буду создавать каталоги для каждой сборки. Я не думаю, что вам нужно разделить их на этом уровне.

3. Полезно ли иметь интерфейсы и абстрактные классы для каждого слоя в своих сборках?

Отчасти идет в ногу с вышеупомянутыми комментариями.

4. Полезно ли иметь сборку "Entities" для бизнес-уровня и уровня данных?

Да. Я бы сказал, что ваши объекты данных могут не отображаться напрямую в соответствии с вашей бизнес-моделью. При хранении данных в базе данных или другом носителе вам может потребоваться изменить ситуацию, чтобы они хорошо воспроизводились. Объекты, которые вы предоставляете своему уровню обслуживания, должны использоваться для пользовательского интерфейса. Объекты, которые вы используете для своего уровня доступа к данным, должны быть пригодны для вашего носителя данных. AutoMapper определенно ваш друг и может помочь с отображением, как вы упоминали. Вот как это выглядит:

Service Layer Details
(источник: microsoft.com)

Ответ 2

Я бы не согласился с этой стандартной многоуровневой архитектурой в пользу луковой архитектуры.

enter image description here

В соответствии с этим я могу попытаться ответить на ваши вопросы:

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

Да, я бы согласился, что это не плохое соглашение, и довольно стандартное.

2. Полезно ли разбить бизнес и слои данных на несколько сборок?

Да, но у меня есть одна сборка под названием Domain (обычно Core.Domain), а другая - Data (Core.Data). Доменная сборка содержит все сущности (в соответствии с управлением доменом) вместе с интерфейсами репозитория, службами, фабриками и т.д. Сбор данных связан с доменом и реализует конкретные репозитории с ORM.

3. Полезно ли иметь интерфейсы и абстрактные классы для каждого слоя в своих собственных сборках?

В зависимости от разных причин. В ответе на предыдущий вопрос я упомянул о разделении интерфейсов для репозиториев в домене и конкретных репозиториях в сборке данных. Это дает вам чистый домен без какого-либо "загрязнения" от каких-либо конкретных данных или любой другой технологии. Как правило, я основываю свой код, думая о TDD-ориентированном уровне, извлекая все зависимости от классов, делая их более пригодными для использования, следуя принципу SRP и думая, что может пойти не так, когда другие люди в команде используют архитектуру:) Например, одно большое преимущество разделения на сборки заключается в том, что вы контролируете свои ссылки и четко указываете "нет кода доступа к данным в домене!".

4. Полезно ли иметь сборку "Сущности" как для бизнеса, так и для слоев данных?

Я бы не согласился и сказал "нет". У вас должны быть ваши основные объекты и сопоставить их с базой данных через ORM. Если у вас сложная логика представления, вы можете иметь что-то вроде объектов ViewModel, которые в основном являются сущностями, только с данными, подходящими для представления в пользовательском интерфейсе. Если у вас есть что-то вроде сети между ними, вы можете также иметь специальные объекты DTO, чтобы минимизировать сетевые вызовы. Но, я думаю, что данные и отдельные бизнес-единицы просто усложняют этот вопрос.

Также, чтобы добавить здесь, если вы начинаете новую архитектуру, и вы говорите о приложении, которое уже существует в течение 10 лет, вам следует рассмотреть лучшие инструменты ORM из LINQ-to-SQL, либо Entity Framework, либо NHibernate (я выбираю NHibernate, на мой взгляд).

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

Ответ 3

1) Именование абсолютно прекрасное, как указано в SwDevMan81.

2) Абсолютно, если WCF устареет через несколько лет, вам нужно будет только изменить свой DAL.

3) Эмпирическое правило состоит в том, чтобы задать себе этот простой вопрос: "Могу ли я придумать случай, когда я буду использовать это?".
 Говоря о ваших контрактах WCF, да, определенно помещаем их в отдельную сборку: это ключ к хорошему дизайну WCF (я расскажу подробнее).
 Говоря об интерфейсе, определенном в AssemblyA, и реализуется в AssemblyB, тогда свойства/методы, описанные в этих интерфейсах, используются в AssemblyC, вы в порядке, если каждый класс, определенный в AssemblyB, используется в C через интерфейс. В противном случае вам придется ссылаться как на A, так и на B: вы теряете.

4) Единственная причина, по которой я могу думать о том, чтобы фактически перемещаться в 3 раза с одним и тем же выглядящим объектом, - это плохой дизайн: отношения с базой данных были плохо обработаны, и поэтому вам нужно настроить объекты, которые выходят, чтобы иметь что-то, что вы можете работайте с.
 Если вы переделаете архитектуру, у вас может быть другая сборка, используемая почти во всех проектах под названием "Объекты", которые хранят объекты данных. По каждому проекту я имел в виду WCF.

На стороне примечания я бы добавил, что служба WCF должна быть разделена на 3 сборки: ServiceContracts, сама Служба и сущности, о которых мы говорили. У меня было хорошее видео в этом последнем пункте, но это на работе, я свяжу его завтра!

НТН,

баб.

EDIT: здесь - это видео.