Я хотел бы, чтобы некоторый совет относительно лучшего подхода использовал в следующей ситуации...
У меня будут Приложение Windows и веб-приложение (уровни представления), они оба получат доступ к общему бизнес-слою. Бизнес-слой посмотрит на конфигурационный файл для нахождения названия dll (слой данных), на который это создаст ссылку во времени выполнения (действительно ли это - лучший подход?).
Причина создания ссылки во времени выполнения к уровню доступа к данным состоит в том, потому что приложение будет взаимодействовать через интерфейс с другой сторонней системой учета в зависимости от того, что использует клиент. Таким образом, у меня был бы отдельный уровень доступа к данным для поддержки каждой системы учета. Они могли быть отдельными проектами установки, каждый клиент будет использовать один или другой, они не должны были бы переключаться между двумя.
Проекты:
MyCompany.Common.dll - Содержит интерфейсы, все другие проекты имеют ссылку на этого.
MyCompany.Windows.dll - Проект Windows Forms, ссылки MyCompany.Business.dll
MyCompany.Web.dll - Проект веб-сайта, ссылки MyCompany.Business.dll
MyCompany.Busniess.dll - Бизнес-Слой, ссылки MyCompany. Данные.* (во времени выполнения)
Уровень MyCompany.Data.AccountingSys1.dll - Data для системы учета 1 уровень MyCompany.Data.AccountingSys2.dll - Data для системы учета 2
MyCompany.Common.dll проекта содержал бы все интерфейсы, друг друга, проект будет иметь ссылку на этого.
Public Interface ICompany
ReadOnly Property Id() as Integer
Property Name() as String
Sub Save()
End Interface
Public Interface ICompanyFactory
Function CreateCompany() as ICompany
End Interface
MyCompany.Data.AccountingSys1.dll проекта и MyCompany.Data.AccountingSys2.dll содержали бы классы как следующее:
Public Class Company
Implements ICompany
Protected _id As Integer
Protected _name As String
Public ReadOnly Property Id As Integer Implements MyCompany.Common.ICompany.Id
Get
Return _id
End Get
End Property
Public Property Name As String Implements MyCompany.Common.ICompany.Name
Get
Return _name
End Get
Set(ByVal value as String)
_name = value
End Set
End Property
Public Sub Save() Implements MyCompany.Common.ICompany.Save
Throw New NotImplementedException()
End Sub
End Class
Public Class CompanyFactory
Implements ICompanyFactory
Public Function CreateCompany() As ICompany Implements MyCompany.Common.ICompanyFactory.CreateCompany
Return New Company()
End Function
End Class
MyCompany.Business.dll проекта предоставил бы бизнес-правила и получил бы форму данных слой данных:
Public Class Companies
Public Shared Function CreateCompany() As ICompany
Dim factory as New MyCompany.Data.CompanyFactory
Return factory.CreateCompany()
End Function
End Class
Любые мнения/предложения значительно ценились бы.
Несколько комментариев.
Я бы не хотел использовать сборку MyCompany.Common.dll
. Обычно они заполняются всевозможными несвязанными вещами, которые затем меняются, часто требуя перестройки всех ваших сборок.
Я бы назвал ваши сборки по имени приложения, а также по названию компании. MyCompany.MyApplication.Business.dll
предпочтительнее MyCompany.Business.dll
. Тогда проще разделить приложения на части и повторно использовать код из нескольких приложений.
Лучше всего иметь отдельные сборки контракта для каждого типа сборки реализации, которую вы собираетесь иметь. В вашем случае я бы предложил следующее:
MyCompany.MyApplication.Windows-Contract.dll
MyCompany.MyApplication.Windows.dll
MyCompany.MyApplication.Web-Contract.dll
MyCompany.MyApplication.Web.dll
MyCompany.MyApplication.Business-Contract.dll
MyCompany.MyApplication.Business.dll
MyCompany.MyApplication.Data-Contract.dll
MyCompany.MyApplication.Data.AccountingSys1.dll
MyCompany.MyApplication.Data.AccountingSys2.dll
Из вашего описания видно, что сборки AccountingSys1
и AccountingSys2
имеют общий контракт, следовательно, только одна сборка контракта для двух сборок реализации.
Контрактные сборки должны отражать ваш дизайн, а не вашу реализацию, и изменяться только из-за изменений дизайна. Вам следует избегать любого «значимого» кода (чтобы избежать ошибок), и вы должны ограничить код интерфейсами, перечислениями, исключениями, атрибутами, аргументами событий и структурами - и все это без «значимого» кода.
При настройке ссылок на сборки следует убедиться, что сборки всегда ссылаются только на сборки контрактов, например:
Data.AccountingSys1
Data-Contract
Data.AccountingSys2
Data-Contract
Business
Business-Contract
Data-Contract
Windows
Windows-Contract
Business-Contract
Data-Contract (maybe)
Web
Web-Contract
Business-Contract
Data-Contract (maybe)
В результате сборки реализации никогда не зависят от других сборок реализации. Когда реализация изменяется, вам нужно перестроить только одну сборку.
Исключением из этого правила является создание иерархий наследования. Например, вы можете создать *. Data.AccountingSys.dll
для определения базовых классов для двух конкретных сборок системы учета.
Если вы можете следовать всему вышеперечисленному, вам нужно будет реализовать какой-то подход с внедрением зависимостей, чтобы иметь возможность создавать экземпляры объектов из интерфейсов в сборках контрактов. Вы можете использовать существующую структуру DI или создать третий набор сборок * - Factory.dll
, содержащих ваши фабричные методы.
Еще одним преимуществом такой структуры является то, что модульное тестирование намного проще и может основываться на контрактах, а не на реализации, что помогает вам писать чистый, тестируемый код.
Это может показаться большим количеством сборок, но преимущества, которые вы получаете от предотвращения создания вредоносных зависимостей в вашем коде, значительно снизят вероятность того, что ваш проект станет слишком сложным, и помогут добиться хорошего качества в процессе работы. Немного боли сейчас устранит такую боль позже.
Ваш общий подход хорош :)
Вы могли бы рассмотреть возможность размещения всех интерфейсов в отдельной сборке (dll) (строго говоря, интерфейс находится между бизнес-логикой и реализацией доступа к данным - они должны быть единственными вещами, которые иметь доступ к интерфейсу), но по большому счету это может не иметь большого значения.
Лично у меня был бы один общий фабричный метод, который возвращал бы объект и просто приводил его соответствующим образом при использовании.
Это отличный подход! Я использовал его в одной из наших систем на работе, и он оказался надежным, восточным для обслуживания и позволяет нам быстро добавлять дополнительные интерфейсы, когда это необходимо (например, когда нам нужно взаимодействовать с другой системой учета от компании, которую мы приобрели).