.NET Application Architecture. Какая сборка является оптимальной для этого класса? - PullRequest
2 голосов
/ 25 января 2011

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

Я потратил некоторое время на рефакторинг, в частности на перенос кода доступа к данным в выделенный проект доступа к данным «Company.DataAccess» (например). Другие логические части кода также имеют свои собственные сборки.

Что меня не устраивает, так это размещение объектов, которые необходимо разделить между сборками:

Например, в моем проекте "Company.ClientDataImport" у меня есть классы, содержащие бизнес-логику для импорта данных клиента.

Одна особенность функциональности, которая включает этот код, заключается в том, что формат импорта клиента может быть сопоставлен с нашим форматом импорта по умолчанию. Итак, у меня есть класс «DataImportFieldMappingInfo» в сборке «Company.ClientDataImport»:

public class DataImportFieldMappingInfo {

    int CompanyFieldName
    {
        get;
        set;
    }

    int ClientFieldName
    {
        get;
        set;
    }
}

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

Одной из целей архитектуры является то, что все операции ввода-вывода базы данных должны обрабатываться "Company.DataAccess".

Я хочу, чтобы «Company.DataAccess» знал о классе DataImportFieldMappingInfo, чтобы он мог использовать класс так, как это было предназначено для хранения данных этого типа, но это означает, что «Company.DataAccess» и «Company.ClientDataImport» оба необходимо знать о DataImportFieldMappingInfo, чтобы они могли общаться с использованием одних и тех же классов.

Мое решение этой распространенной проблемы до сих пор заключается в использовании другой библиотеки под названием «Company.DomainEntities», которая содержит классы для объектов, которые совместно используются другими сборками в приложении. Это работает хорошо, и все сборки могут взаимодействовать в рамках одних и тех же объектов. Классы на самом деле просто содержат свойства, поэтому они являются просто контейнерами данных, а не логикой приложения.

Что мне не нравится в этом, так это то, что DataImportFieldMappingInfo представляет собой проблему импорта данных, и поэтому я считаю, что она должна находиться в этом пространстве имен и, следовательно, в проекте «Company.ClientDataImport». Однако ограничения круговой ссылки препятствуют этому, поэтому я должен использовать проект Company.DomainEntities в качестве посредника.

Что-то не так с моей архитектурой, или это обычная практика в такой ситуации?

Ответы [ 2 ]

2 голосов
/ 25 января 2011

Я склонен согласиться с Марком; но есть пара других вещей, которые вы могли бы рассмотреть:

Что представляет собой «бизнес-логика» и «доступ к данным» по сравнению с простыми структурами данных, о которых ваше приложение «знает»? На первый взгляд DataImportFieldMappingInfo выглядит так, как будто это может быть связано с доступом к данным, но я думаю, что они на самом деле являются просто общими структурами (и более ориентированы на бизнес), поэтому их объединение в единую сборку имеет смысл - если вы используете их для обмена данными между различными слоями / assembilies.

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

Редактировать:

Короче говоря, интерфейсы позволяют вам абстрагироваться от сложности и зависимостей, связанных с неприятными вещами, с которыми вы бы не хотели быть тесно (и постоянно) связанными.

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

Идея состоит в том, что вместо того, чтобы ваша бизнес-логика (сборка / ы) зависела от доступа к данным, у вас обоих они зависят от интерфейса; Затем вы можете поменять реализацию в любое время. У вас может быть «реальный» компонент доступа к данным, который работает против SQL, и «фиктивный», который возвращает фиктивные тестовые данные прямо из вашего кода.

Когда вы разрабатываете интерфейс, идея состоит в том, чтобы проектировать его с точки зрения бизнес-логики / домена, а не с технической точки зрения; хотя бывают случаи, когда это уместно. Задача реализации (т. Е. Класс доступа к данным, который реализует интерфейс) заключается в преобразовании интерфейса, ориентированного на бизнес, и любого странного источника данных, с которым он имеет дело. Например, ваш средний поставщик данных на основе SQL может высосать данные через DataReader и преобразовать в MyBusiness.Common.Info.OutstandingAccountsCollection.

Структура кода

  • Common Assembly (MyBusiness.Common) - содержит любые структуры данных, которые используются для обмена данными между вашими слоями (например, классы MyBusiness.Common.Info.OutstandingAccount и MyBusiness.Common.Info.OutstandingAccountCollection).
  • Интерфейсные сборки: у вас может быть несколько из них. Если весь ваш доступ к данным осуществляется через одну систему (скажем, SQL), то, вероятно, у вас будет только одна (MyBusiness.Interfaces.DataAccessProvider), но если у вас есть различные системы для взаимодействия с ними, вы захотите сохранить их в отдельности. MyBusiness.Interfaces.DataAccessProvider ссылается на общую сборку, потому что она будет включать типы 'info' в свои контракты.
  • Конкретные сборки доступа к данным: (MyBusiness.DataAccess.SQLDataAccessProvider). это относится к сборке Common и Interface.
  • Your Business Logic (MyBusiness.BusinessLogic) ссылается на сборки Common и Interface.

Для получения дополнительной информации (и диаграмм) ознакомьтесь с этой частью (оповещение о саморекламе): 5-слойная архитектура

2 голосов
/ 25 января 2011

В этом сценарии разбиение общих типов / интерфейсов на отдельную сборку является вполне нормальным.

Другой подход, не неслыханный, состоит в том, чтобы просто сказать «все эти сборки не делают ничего лишнего» и просто использовать пространства имен для разделения кода. Если вам не нужно , нужно , чтобы иметь возможность работать только с подмножеством dll, почему бы просто не иметь one dll? Классическим контрапунктом этому является то, что у вас есть несколько различных UI / сервисов, каждый из которых использует одну и ту же DLL (или разные подмножества DLL).

...