7 votes

Architecture N-Tier - Structure avec plusieurs projets en VB.NET

J'aimerais avoir des conseils sur la meilleure approche à adopter dans la situation suivante...

Je disposerai d'une application Windows et d'une application Web (couches de présentation), qui accéderont toutes deux à une couche métier commune. La couche métier consultera un fichier de configuration pour trouver le nom de la dll (couche de données) vers laquelle elle créera une référence au moment de l'exécution (est-ce la meilleure approche ?).

La raison pour laquelle la référence à la couche d'accès aux données est créée au moment de l'exécution est que l'application s'interfacera avec un système comptable tiers différent en fonction de ce que le client utilise. J'aurais donc une couche d'accès aux données distincte pour chaque système de comptabilité. Il pourrait s'agir de projets d'installation distincts, chaque client utiliserait l'un ou l'autre, il n'aurait pas besoin de passer d'un système à l'autre.

Projets :

MyCompany.Common.dll - Contient des interfaces, tous les autres projets ont une référence à celui-ci.
MyCompany.Windows.dll - Projet Windows Forms, références MyCompany.Business.dll
MyCompany.Web.dll - Projet de site web, références MyCompany.Business.dll
MyCompany.Busniess.dll - Business Layer, références MyCompany.Data.* (au moment de l'exécution)
MyCompany.Data.AccountingSys1.dll - Couche de données pour le système comptable 1 MyCompany.Data.AccountingSys2.dll - Couche de données pour le système comptable 2

Le projet MyCompany.Common.dll contiendrait toutes les interfaces, chaque autre projet aurait une référence à celui-ci.

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

Le projet MyCompany.Data.AccountingSys1.dll y MyCompany.Data.AccountingSys2.dll contiendrait les classes suivantes :

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

Le projet MyCompany.Business.dll fournira les règles de gestion et récupérera les données de la couche de données :

Public Class Companies

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

End Class

Tout avis/suggestion serait grandement apprécié.

5voto

Enigmativity Points 26345

Quelques commentaires.

J'éviterais d'avoir un MyCompany.Common.dll l'assemblée. Ceux-ci finissent généralement par être remplis de toutes sortes de choses sans rapport les unes avec les autres, qui sont ensuite modifiées, ce qui nécessite souvent une reconstruction de tous les assemblages.

Je nommerais vos assemblages avec le nom de l'application ainsi que le nom de l'entreprise. MyCompany.MyApplication.Business.dll est préférable à MyCompany.Business.dll . Il est alors plus facile de diviser les applications en sous-parties et de réutiliser le code de plusieurs applications.

Il est préférable d'avoir des assemblages de contrats distincts pour chaque type d'assemblage d'implémentation que vous allez avoir. Dans votre cas, je suggérerais ce qui suit :

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

D'après votre description, il semble que le AccountingSys1 y AccountingSys2 partagent un contrat commun, ce qui explique qu'il n'y ait qu'un seul contrat pour les deux assemblages de mise en œuvre.

Les assemblages contractuels doivent représenter votre conception, et non votre mise en œuvre, et ne doivent être modifiés qu'en cas de changement de conception. Vous devez éviter tout code "significatif" (pour éviter les bogues) et vous devez limiter le code aux interfaces, aux enums, aux exceptions, aux attributs, aux args d'événements et aux structures - le tout sans code "significatif".

Lorsque vous définissez les références des assemblages, vous devez veiller à ce que les assemblages ne fassent jamais référence qu'à des assemblages contractuels, comme suit :

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)

Par conséquent, les assemblages d'implémentation ne dépendent jamais d'autres assemblages d'implémentation. Lorsqu'une implémentation change, vous n'avez qu'un seul assemblage à reconstruire.

L'exception à cette règle est la création de hiérarchies d'héritage. Par exemple, vous pouvez créer un *.Data.AccountingSys.dll pour définir des classes de base pour les deux assemblages spécifiques du système de comptabilité.

Si vous pouvez suivre toutes les instructions ci-dessus, vous devrez alors mettre en œuvre une sorte d'approche d'injection de dépendance pour pouvoir créer des instances d'objets à partir des interfaces dans les assemblages de contrats. Vous pouvez utiliser un cadre d'injection de dépendances existant ou créer un troisième ensemble d'assemblages de contrats. *-Factory.dll qui contiennent vos méthodes d'usine.

Un autre avantage de ce type de structure est que les tests unitaires sont beaucoup plus simples et peuvent être basés sur les contrats plutôt que sur l'implémentation, ce qui vous aide à écrire un code propre et testable.

Cela peut sembler beaucoup d'assemblages, mais les avantages que vous obtenez en empêchant votre code de créer des dépendances désagréables réduiront considérablement le risque que votre projet devienne trop complexe et vous aideront à obtenir une bonne qualité au fur et à mesure que vous avancez. Un peu de souffrance aujourd'hui éliminera beaucoup de souffrance plus tard.

1voto

Adrian K Points 5124

Votre approche générale est bonne :)

Vous pourriez envisager de placer toutes les interfaces dans un assemblage séparé (dll) (à proprement parler, l'interface se situe entre la logique commerciale et l'implémentation de l'accès aux données - elles devraient être les seules à avoir accès à l'interface), mais dans le grand schéma des choses, ce n'est peut-être pas une si grande affaire.

Personnellement, j'aurais une méthode d'usine partagée qui renverrait un objet, et je la coulerais de manière appropriée lorsqu'elle est utilisée.

1voto

Michael Rodrigues Points 2932

C'est une excellente approche ! Je l'ai utilisée dans l'un de nos systèmes au travail et elle s'est avérée fiable, facile à maintenir et nous permet d'ajouter rapidement des interfaces supplémentaires en cas de besoin (par exemple, lorsque nous devons nous interfacer avec un autre système de comptabilité d'une société que nous avons acquise).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X