N 层架构 - VB.NET 中具有多个项目的结构

发布于 2024-08-26 03:30:15 字数 2319 浏览 5 评论 0原文

我想要一些关于在以下情况下使用的最佳方法的建议...

我将有一个 Windows 应用程序和一个 Web 应用程序(表示层),它们都将访问公共业务层。业务层将查看配置文件以查找 dll(数据层)的名称,它将在运行时创建对它的引用(这是最好的方法吗?)。

在运行时创建对数据访问层的引用的原因是,应用程序将根据客户端使用的内容与不同的第三方会计系统进行交互。所以我会有一个单独的数据访问层来支持每个会计系统。这些可以是单独的设置项目,每个客户都会使用其中之一,他们不需要在两者之间切换。

项目:

MyCompany.Common.dll - 包含接口,所有其他项目都引用此接口。
MyCompany.Windows.dll - Windows 窗体项目,引用 MyCompany.Business.dll
MyCompany.Web.dll - 网站项目,引用 MyCompany.Business.dll
MyCompany.Busniess.dll - 业务层,引用 MyCompany.Data.*(运行时)
MyCompany.Data.AccountingSys1.dll - 会计系统 1 的数据层 MyCompany.Data.AccountingSys2.dll - 会计系统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.dllMyCompany.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< /strong> 将提供业务规则并从数据层检索数据:

Public Class Companies

    Public Shared Function CreateCompany() As ICompany
        Dim factory as New MyCompany.Data.CompanyFactory
        Return factory.CreateCompany()
    End Function    

End Class

任何意见/建议将不胜感激。

I would like some advice on the best approach to use in the following situation...

I will have a Windows Application and a Web Application (presentation layers), these will both access a common business layer. The business layer will look at a configuration file to find the name of the dll (data layer) which it will create a reference to at runtime (is this the best approach?).

The reason for creating the reference at runtime to the data access layer is because the application will interface with a different 3rd party accounting system depending on what the client is using. So I would have a separate data access layer to support each accounting system. These could be separate setup projects, each client would use one or the other, they wouldn't need to switch between the two.

Projects:

MyCompany.Common.dll - Contains interfaces, all other projects have a reference to this one.
MyCompany.Windows.dll - Windows Forms Project, references MyCompany.Business.dll
MyCompany.Web.dll - Website project, references MyCompany.Business.dll
MyCompany.Busniess.dll - Business Layer, references MyCompany.Data.* (at runtime)
MyCompany.Data.AccountingSys1.dll - Data layer for accounting system 1
MyCompany.Data.AccountingSys2.dll - Data layer for accounting system 2

The project MyCompany.Common.dll would contain all the interfaces, each other project would have a reference to this one.

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

The project MyCompany.Data.AccountingSys1.dll and MyCompany.Data.AccountingSys2.dll would contain the classes like the following:

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

The project MyCompany.Business.dll would provide the business rules and retrieve data form the data layer:

Public Class Companies

    Public Shared Function CreateCompany() As ICompany
        Dim factory as New MyCompany.Data.CompanyFactory
        Return factory.CreateCompany()
    End Function    

End Class

Any opinions/suggestions would be greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

姜生凉生 2024-09-02 03:30:15

一些评论。

我会避免使用 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

根据您的描述,AccountingSys1AccountingSys2 程序集共享一个通用合同,因此两个实现程序集只有一个合同程序集。

合同程序集应代表您的设计,而不是您的实现,并且仅因设计更改而更改。您应该避免使用任何“重要”代码(以避免错误),并且应该将代码限制为接口、枚举、异常、属性、事件参数和结构 - 所有这些都没有“重要”代码。

设置程序集引用时,应确保程序集仅引用契约程序集,如下所示:

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 程序集。

这种结构的另一个好处是单元测试更加简单,并且可以基于契约而不是实现,帮助您编写干净的、可测试的代码。

这可能看起来像很多程序集,但是通过防止代码创建令人讨厌的依赖项所获得的好处将显着减少项目变得过于复杂的机会,并有助于在项目进行过程中提高质量。现在的一点疼痛会消除以后的大量疼痛。

A few comments.

I would avoid having a MyCompany.Common.dll assembly. These typically end up getting filled with all sorts of unrelated things which then get changed often requiring a rebuild of all of your assemblies.

I would name your assemblies with the application name as well as the company name. MyCompany.MyApplication.Business.dll is preferable to MyCompany.Business.dll. It is then easier to split applications into sub parts and to reuse code from multiple applications.

It's best to have separate contract assemblies for each type of implementation assembly you're going to have. In your case I would suggest the following:

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

From your description it appears that the AccountingSys1 and AccountingSys2 assemblies share a common contract hence only one contract assembly for the two implementation assemblies.

Contract assemblies should represent your design, not your implementation, and only change because of design changes. You should avoid having any "significant" code (to avoid bugs) and you should constrain the code to interfaces, enums, exceptions, attributes, event args, and structs - all with no "significant" code.

When setting up assembly references you should ensure that assemblies only ever reference contract assemblies, like so:

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)

As a result implementation assemblies never have a dependency on other implementation assemblies. When an implementation changes you only have one assembly to rebuild.

The exception to this rule is when creating inheritance hierarchies. For example, you may create a *.Data.AccountingSys.dll to define base classes for the two specific accounting system assemblies.

If you can follow all of the above then you will need to implement some sort of dependency injection approach to be able to create instances of objects from the interfaces in the contract assemblies. You could use an existing DI framework or create a third set of *-Factory.dll assemblies that contain your factory methods.

A further benefit of this kind of structure is that unit testing is much simpler and can be based on the contracts rather than the implementation, helping you to write clean, testable code.

This may seem like a lot of assemblies, but the benefits you get from keeping your code from creating nasty dependencies will significantly reduce the chance that your project will become too complex and will help drive good quality as you go. A little pain now will eliminate so much pain later.

离笑几人歌 2024-09-02 03:30:15

您的一般方法是合理的:)

您可以考虑将所有接口放在单独的程序集(dll)中(严格来说,接口位于业务逻辑和数据访问实现之间 - 它们应该是唯一有权访问接口的东西) ,但从长远来看,这可能没什么大不了的。

就我个人而言,我有一个返回对象的共享工厂方法,并在使用时适当地转换它。

Your general approach is sound :)

You could consider putting all the interfaces in a seperate assembly (dll) (strictly speaking the interface sits between the business logic and the data access implementation - they should be the only things that have access to the interface), but in the grand scheme of things that might not be such a big deal.

Personally I'd have one shared factory method that returned an object, and just cast it appropriately when used.

怎言笑 2024-09-02 03:30:15

这是一个很棒的方法!我已经在我们的一个工作系统中使用了它,事实证明它可靠、易于维护,并且允许我们在需要时快速添加额外的接口(例如,当我们需要与我们收购的公司的另一个会计系统接口时)。 )

It's an excellent approach! I've use it in one of our systems at work, and it has proved reliable, east to maintain and allows us to quickly add extra interfaces when needed (e.g. when we need to interface with another accounting system from a company that we acquired.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文