N-Tier Architecture - A Multi-Project Framework in VB.NET

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

I will have a Windows application and a web application (presentation tiers), they will both have access to a common business tier. The business layer will search the configuration file to find the name of the DLL (data layer) that will create the link at runtime (is this the best approach?).

The reason for creating a link at runtime at the data access level is because the application will interact with another third-party accounting system, depending on what the client uses. Therefore, I will have a separate level of data access to support each accounting system. These can be separate configuration projects, each client will use one or the other, they will not need to switch between them.

Projects

MyCompany.Common.dll - contains interfaces, all other projects have a link to this. MyCompany.Windows.dll - Windows Forms project, MyCompany.Business.dll links
MyCompany.Web.dll - website project, links MyCompany.Business.dll
MyCompany.Busniess.dll - business layer, MyCompany.Data links. * (At runtime)
MyCompany.Data.AccountingSys1.dll - Data level for accounting system 1 MyCompany.Data.AccountingSys2.dll - Data level for accounting system 2

The MyCompany.Common.dll project will contain all interfaces, each other project will have a link to this file.

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 will contain the following classes:

 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 MyCompany.Business.dll project will provide business rules and extract data from 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 are welcome.

+7
architecture n-tier
source share
3 answers

A few comments.

I would avoid building MyCompany.Common.dll . Usually they end up filling in all kinds of unrelated things, which then change, which often requires restoring all your assemblies.

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

It is better to have separate contract assemblies for each type of assembly assembly that you will 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 turns out that the AccountingSys1 and AccountingSys2 assemblies have a common contract, therefore, only one contract assembly for two implementation assemblies.

Contract builds should represent your design, not your implementation, and only changes due to design changes. You should avoid having “significant” code (to avoid errors), and you should limit code to interfaces, enumerations, exceptions, attributes, event arguments, and structures — all without “significant” code.

When setting up assembly references, you must ensure that assemblies only ever reference assembly nodes, for example:

 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 depend on other implementation assemblies. When you change the implementation, you have only one assembly to restore.

An exception to this rule is the creation of an inheritance hierarchy. For example, you can create *.Data.AccountingSys.dll to determine the base classes for two specific accounting system assemblies.

If you can follow all the above, you will need to implement some kind of dependency recharging approach in order to be able to instantiate objects from interfaces in contract assemblies. You can use the existing DI infrastructure or create a third set of *-Factory.dll assemblies that contain your factory methods.

Another advantage of this structure is that unit testing is much simpler and can be based on contracts rather than implementation, helping you write clean, testable code.

This may seem like a lot of builds, but the benefits that you get from saving the code from creating unpleasant dependencies will greatly reduce the likelihood that your project will become too complex and will help the driver of good quality as you progress. A little pain will now eliminate so much pain later.

+5
source share

Your general approach sounds :)

You can consider placing all the interfaces in a separate assembly (dll) (strictly speaking, the interface is between the business logic and the implementation of data access - they should be the only things that have access to the interface), but in a great scheme of things that can to be not so big.

Personally, I would have one common factory method that would return an object, and just apply it when used.

+1
source share

This is a great approach! I use it in one of our systems at work, and it has proven itself to be reliable, the East has supported and allows us to quickly add additional interfaces when necessary (for example, when we need to interact with another accounting system from the company we acquired.)

+1
source share

All Articles