Structurer le projet
Le Lino structure les solutions .NET afin que les décisions architecturales soient explicites dÚs le premier commit. Un projet généré démarre avec l'orchestration Aspire, des blocs partagés, des tests, une configuration de qualité du code et un emplacement clair pour chaque service, module, contrat d'API, préoccupation de persistance et limite d'intégration.
Cette section explique comment lino project new, lino service new et lino module new façonnent la solution. L'objectif n'est pas seulement de créer des dossiers, mais de définir des limites de runtime, l'ownership de la base de données, l'isolation des modules et un chemin d'évolution d'un service simple vers un monolithe modulaire ou un systÚme distribué.
Créer la fondation de la solution
La commande lino project new crée la fondation technique d'une nouvelle solution .NET. Exécutez-la dans un répertoire vide aprÚs avoir installé et authentifié la CLI.
lino project new --name
L'argument représente le nom réel de la solution. Ce nom entre dans la composition des namespaces, assemblies, chemins, configurations, artefacts et références entre composants ; choisissez donc un nom court, stable et représentatif.
L'assistant interactif demande des décisions qui affectent la structure et le comportement de runtime de toute la solution :
- Namespace du projet : identité technique racine utilisée par les projets générés.
- Nom d'affichage : nom convivial utilisé dans les métadonnées générées et les points visibles par l'utilisateur.
- Langage et stack : actuellement C# avec .NET 10 et Aspire.
- Analyseurs de code : activent des packages et rÚgles partagés pour maintenir cohérence et qualité dÚs le bootstrap.
- CQRS : prépare la couche d'application à séparer commands et queries, avec orchestration par la bibliothÚque de mediator sélectionnée.
- Classes de base dans la solution : contrÎle si des abstractions communes seront générées localement dans la solution.
- Cache distribué : définit si
Microsoft.Extensions.Caching.Hybridutilisera seulement la mémoire locale de l'instance ou aussi une couche distribuée avec Redis configurée par Aspire. - Communication asynchrone : active RabbitMQ avec MassTransit et les blocs de messagerie/outbox utilisés par les événements d'intégration.
- Langue des données : langue utilisée pour décrire les métadonnées de domaine pendant la génération.
- Cultures prises en charge par l'application : ressources de localisation générées pour les textes d'UI, validations, erreurs et réponses d'API.
- Culture par défaut : langue principale utilisée lorsque l'application a besoin d'un fallback.
AprÚs confirmation, Lino génÚre une solution organisée pour la croissance. Un projet minimal démarre avec Aspire, des couches partagées et les tests de la zone Shared :
/ âââ .slnx âââ Directory.Build.props âââ Directory.Packages.props âââ src/ â âââ Aspire/ â â âââ AppHost/ â â â âââ .AppHost.csproj â â âââ ServiceDefaults/ â â âââ .ServiceDefaults.csproj â âââ Services/ â âââ Shared/ â âââ Api/ â â âââ .Shared.Api.csproj â âââ Application/ â â âââ .Shared.Application.csproj â âââ Domain/ â â âââ .Shared.Domain.csproj â âââ Infrastructure/ â â âââ .Shared.Infrastructure.csproj â âââ Infrastructure.Persistence/ â â âââ .Shared.Infrastructure.Persistence.csproj â âââ Integration.Events/ (quand la messagerie existe) â âââ .Shared.Integration.Events.csproj âââ tests/ âââ Services/ âââ Shared/ âââ UnitTests/ âââ Domain/ â âââ .Shared.Domain.UnitTests.csproj âââ Application/ âââ .Shared.Application.UnitTests.csproj
Le rĂŽle des projets Shared
Shared contient le code de plateforme partagé par la solution. Ce n'est pas un service métier. Utilisez cet espace pour les abstractions transversales, les erreurs communes, l'infrastructure de localisation, les contrats de base de l'application, les helpers de persistance, les extensions d'API, l'intégration avec l'host, l'observabilité et les utilitaires techniques réutilisables.
Ăvitez de placer des rĂšgles mĂ©tier dans Shared. Si une rĂšgle appartient Ă une capacitĂ© spĂ©cifique de l'application, elle doit rester dans le service ou le module qui possĂšde cette responsabilitĂ©. L'objectif de Shared est de rĂ©duire la rĂ©pĂ©tition technique, pas de devenir un raccourci pour coupler diffĂ©rents domaines.
Aspire et décisions d'infrastructure
AppHost compose la solution et ses ressources de runtime. Lorsque Redis, RabbitMQ, SQL Server, PostgreSQL, Redis Insight, des services, WebApps ou workers sont ajoutés, Aspire devient le point d'orchestration local. Cela simplifie l'exécution, la découverte de services, les logs, métriques, traces et la visualisation des ressources pendant le développement.
ServiceDefaults centralise des patterns d'hosting tels que service discovery, health checks, résilience, logging, métriques, tracing et intégration OpenTelemetry. Au lieu que chaque service configure cela isolément, la solution démarre avec un point commun de composition.
Analyseurs de code
Les analyseurs de code statique inspectent le code pendant le développement et rendent les problÚmes visibles avant l'exécution : incohérences de style, patterns fragiles, opportunités de refactoring, bugs possibles et alertes de sécurité.
Lorsque vous activez les analyseurs dans lino project new, la solution démarre déjà avec des packages comme StyleCop.Analyzers, SonarAnalyzer.CSharp et Roslynator.Analyzers configurés de maniÚre centralisée. Cela évite que chaque projet doive décider seul des rÚgles à suivre.
- Amélioration de la qualité : garde le code lisible, cohérent et aligné avec les standards de la solution.
- Prévention des erreurs : signale les problÚmes tÎt, avant qu'ils n'atteignent les tests manuels ou la production.
- Standardisation : réduit les différences de style entre services, modules et équipes.
- Refactoring assisté : met en évidence des simplifications et modernisations possibles dans le code C#.
Cache distribué, cache hybride et Redis
Lino prĂ©pare la solution Ă utiliser Microsoft.Extensions.Caching.Hybrid, la bibliothĂšque Microsoft qui centralise les opĂ©rations de cache via HybridCache. Cette abstraction permet aux handlers, services d'application et composants d'infrastructure de stocker des rĂ©sultats de requĂȘtes, permissions, configurations ou donnĂ©es de support sans disperser les dĂ©tails d'implĂ©mentation du cache dans le code.
Lorsque vous n'activez pas le cache distribué à la création du projet, HybridCache reste disponible, mais fonctionne uniquement avec la mémoire locale de l'instance en cours d'exécution. Ce mode est simple et suffisant pour les scénarios locaux, les petits environnements ou les applications avec une seule réplique, mais chaque processus conserve son propre cache et les données ne sont pas partagées entre instances.
Lorsque vous activez le cache distribuĂ©, Lino ajoute Redis aux ressources Aspire et configure l'infrastructure pour utiliser une couche de cache partagĂ©e. Avec Redis, plusieurs instances du mĂȘme service peuvent interroger la mĂȘme couche, ce qui rĂ©duit les lectures rĂ©pĂ©tĂ©es en base de donnĂ©es, APIs internes ou intĂ©grations et prĂ©pare le projet Ă la mise Ă l'Ă©chelle horizontale.
- Performance : réduit le temps de réponse pour les lectures répétées et les opérations de support.
- Scalabilité : permet à différentes instances de partager des données en cache.
- Disponibilité : découple une partie de la charge de lecture de la base de données principale.
- CoĂ»t opĂ©rationnel : diminue le traitement rĂ©pĂ©titif dans les requĂȘtes et intĂ©grations Ă fort volume.
Cette décision est prise dans lino project new parce qu'elle modifie la fondation de l'environnement : ressources de l'AppHost, secrets locaux, packages, configuration d'infrastructure et topologie d'exécution affichée dans le dashboard Aspire.
Communication asynchrone
La communication asynchrone permet aux services, modules et composants de réagir à des faits du systÚme sans bloquer le flux principal de l'opération. Elle est particuliÚrement utile lorsque le producteur ne doit pas dépendre de la disponibilité immédiate du consommateur ou lorsqu'une action doit déclencher des effets ultérieurs, comme des notifications, projections, intégrations externes ou synchronisations entre contextes.
Lorsque vous activez la communication asynchrone, Lino ajoute RabbitMQ Ă Aspire et configure MassTransit avec les blocs de messagerie et outbox utilisĂ©s par les Ă©vĂ©nements d'intĂ©gration. Ainsi, les Ă©vĂ©nements publiĂ©s par des services ou modules sont reprĂ©sentĂ©s par des contrats explicites dans Integration.Events et peuvent ĂȘtre traitĂ©s avec plus de rĂ©silience.
- Performance : permet au cas d'utilisation principal de continuer sans attendre que tous les consommateurs aient terminé.
- Scalabilité : distribue le traitement entre consommateurs et files, en absorbant les pics avec plus de contrÎle.
- Résilience : permet le retraitement et réduit la perte de messages lorsqu'elle est combinée avec outbox.
- Découplage : évite la dépendance directe entre producteur et consommateur lorsqu'une réponse immédiate n'est pas nécessaire.
Utilisez des événements d'intégration lorsqu'un échec du consommateur ne doit pas annuler la transaction du producteur. Lorsque le consommateur doit répondre immédiatement pour compléter le cas d'utilisation, préférez une intégration synchrone explicite et traitez disponibilité, timeout et fallback comme faisant partie du contrat.
Localisation et cultures
Lorsque des cultures sont sĂ©lectionnĂ©es, Lino gĂ©nĂšre des ressources afin que messages, validations, labels, erreurs et textes d'UI puissent ĂȘtre localisĂ©s dĂšs le dĂ©but. Cela Ă©vite de traiter l'internationalisation comme un correctif tardif, une fois les chaĂźnes dĂ©jĂ dispersĂ©es dans les endpoints, handlers et composants.
Valider la fondation et passer aux services
AprÚs la génération, restaurez les dépendances, compilez la solution et exécutez l'host Aspire :
dotnet restore.slnx dotnet build .slnx dotnet run --project src/Aspire/AppHost/ .AppHost.csproj
à ce stade, la solution reste une fondation. Ouvrez le projet dans l'éditeur, révisez les ressources créées par Aspire, confirmez les décisions de cache, messagerie, localisation et qualité du code, puis passez aux services qui représenteront les capacités métier de l'application.
Créer et gérer des services
Un service est une limite de runtime et d'ownership. Dans Lino, les services peuvent représenter des APIs indépendantes dans un systÚme distribué ou des zones métier plus larges dans une solution qui peut évoluer progressivement.
AprÚs avoir créé la fondation du projet, ajoutez des services avec :
lino service new
L'assistant de service demande :
- Namespace du service : nom technique utilisé dans les dossiers, projets et namespaces.
- Nom d'affichage : nom convivial du service.
- Type de service : choisissez entre simple et modulaire.
- Provider de base de données : choisissez SQL Server ou PostgreSQL pour la base de données du service.
- Style d'architecture : actuellement Clean Architecture pour les services simples.
- Strongly Typed IDs : dans les services simples, définit si les identifiants seront générés comme types dédiés.
Types de service
Service simple : structure plus directe pour une capacité métier avec une frontiÚre claire. C'est un bon choix pour une API ciblée, un microservice ou une zone assez petite pour évoluer comme une unité.
Service modulaire : structure indiquée pour les systÚmes plus grands, les monolithes modulaires ou les runtimes qui doivent héberger plusieurs capacités indépendantes. Les modules améliorent l'organisation et la scalabilité du modÚle, mais exigent plus de discipline de dépendance.
Quel que soit le type, chaque service possÚde sa propre base de données. Dans les services simples, la décision sur Strongly Typed IDs se prend au niveau du service ; dans les services modulaires, elle se prend par module.
Service simple
Un service simple place ses couches directement dans src/Services/. Utilisez cette structure lorsque la capacitĂ© mĂ©tier possĂšde une limite claire et n'a pas besoin de plusieurs modules isolĂ©s dans le mĂȘme runtime. Elle fonctionne bien pour une API ciblĂ©e, un microservice ou une zone de domaine assez petite pour Ă©voluer comme une unitĂ©.
src/Services// âââ Domain/ â âââ . .Domain.csproj âââ Application/ â âââ . .Application.csproj âââ Infrastructure.Persistence/ â âââ . .Infrastructure.Persistence.csproj âââ Infrastructure/ â âââ . .Infrastructure.csproj âââ Api/ â âââ . .Api.csproj âââ Integration.Events/ (quand la messagerie existe) â âââ . .Integration.Events.csproj âââ Api.Contracts/ (quand la consommation HTTP typĂ©e existe) â âââ . .Api.Contracts.csproj âââ Api.Client/ (quand la consommation HTTP typĂ©e existe) âââ . .Api.Client.csproj tests/Services/ / âââ UnitTests/ â âââ Domain/ â â âââ . .Domain.UnitTests.csproj â âââ Application/ â âââ . .Application.UnitTests.csproj âââ IntegrationTests/ âââ . .IntegrationTests.csproj
Les couches ont des responsabilités distinctes : Domain protÚge les rÚgles métier et invariants ; Application orchestre les cas d'utilisation ; Infrastructure.Persistence contient Entity Framework Core, repositories, Unit of Work et migrations ; Infrastructure contient les intégrations techniques ; et Api adapte HTTP aux cas d'utilisation.
Les projets Api.Contracts et Api.Client apparaissent lorsqu'il existe une consommation HTTP typée, surtout dans les solutions avec Web App Blazor. Le premier concentre requests, responses, DTOs, types publics et interfaces de client partagées ; le second fournit l'implémentation HTTP de ces interfaces, avec HttpClient, afin que Blazor consomme les APIs générées sans dupliquer les contrats.
Le projet Integration.Events apparaßt lorsque la solution a la messagerie activée. Il contient les événements d'intégration publiés et consommés par d'autres modules, services ou systÚmes, généralement avec l'infrastructure de messagerie et outbox.
Service modulaire
Un service modulaire est indiquĂ© pour les monolithes modulaires ou pour un runtime qui hĂ©berge plusieurs bounded contexts. Le service lui-mĂȘme possĂšde l'host, l'infrastructure commune et le provider de base de donnĂ©es ; les rĂšgles mĂ©tier vivent dans les modules.
src/Services// âââ Host/ â âââ . .Host.csproj âââ Infrastructure/ â âââ . .Infrastructure.csproj âââ Modules/ tests/Services/ / âââ Modules/
Le Host compose les modules, configurations, endpoints et l'infrastructure partagée de ce service. Il ne doit pas contenir de rÚgles métier. Le projet Infrastructure au niveau du service fournit le support technique de composition partagé par les modules.
Lorsqu'un module est ajoutĂ©, les projets Domain, Application, Infrastructure.Persistence, Infrastructure et Api apparaissent dans Modules/, en prĂ©servant la frontiĂšre interne. Les projets Api.Contracts, Api.Client et Integration.Events suivent les mĂȘmes conditions : consommation HTTP typĂ©e pour Blazor/API clients et messagerie activĂ©e pour les Ă©vĂ©nements d'intĂ©gration.
Ownership de base de données
Chaque service possÚde sa propre base de données. Dans une solution avec plusieurs services, un service peut utiliser PostgreSQL tandis qu'un autre utilise SQL Server. Cette décision peut varier selon les besoins du domaine, la performance, l'exploitation, la maturité de l'équipe ou l'intégration avec l'infrastructure existante.
Dans un service modulaire, la base appartient au service en tant que runtime, mais les modules sont isolĂ©s par schema, projets de persistance et migrations propres. Un module ne doit pas consulter directement les tables d'un autre module, mĂȘme lorsque les tables se trouvent dans la mĂȘme base physique.
Choisir entre simple et modulaire
Choisissez la plus petite structure qui protÚge la limite. Un service simple suffit lorsqu'il existe une seule frontiÚre de domaine. Un service modulaire a du sens lorsque plusieurs capacités doivent partager runtime, deploy ou transaction, tout en conservant modÚles, persistance, APIs et tests séparés.
| Choix | Quand l'utiliser | Coût assumé |
|---|---|---|
| Service simple | Un domaine ciblé, peu de frontiÚres internes, API isolée ou microservice direct. | Moins de structure initiale, mais moins d'isolation interne si le domaine grandit trop. |
| Service modulaire | Plusieurs sous-domaines dans le mĂȘme runtime, monolithe modulaire, SaaS avec zones indĂ©pendantes ou besoin de schemas par module. | Plus de projets, plus de discipline de dĂ©pendance et davantage d'attention aux contrats internes. |
Style d'architecture
Les services générés par Lino suivent Clean Architecture afin de séparer les rÚgles métier des détails techniques. La couche de domaine n'a pas besoin de connaßtre HTTP, Entity Framework Core, la messagerie, l'UI ou les providers externes ; ces préoccupations restent aux bords de l'application.
- Découplage : les rÚgles centrales ne dépendent pas de frameworks ni de mécanismes de livraison.
- Maintenabilité : les changements d'infrastructure tendent à rester isolés des rÚgles métier.
- TestabilitĂ© : les cas d'utilisation et le domaine peuvent ĂȘtre testĂ©s avec moins de dĂ©pendances externes.
- Ăvolution : les dĂ©tails techniques peuvent ĂȘtre remplacĂ©s avec moins d'impact sur le noyau du service.
Clean Architecture et Strongly Typed IDs
Les services générés suivent Clean Architecture afin que le code métier ne dépende pas de HTTP, EF Core, la messagerie, l'UI ou les détails d'infrastructure. Cette séparation rend les tests plus directs, réduit le couplage et permet de remplacer les détails techniques sans réécrire les rÚgles centrales.
Strongly Typed IDs augmente la sĂ©curitĂ© en empĂȘchant les mĂ©langes accidentels d'identifiants. Au lieu d'accepter n'importe quel Guid, long ou int, le domaine peut travailler avec un type propre Ă chaque entitĂ© ou agrĂ©gat, gĂ©nĂ©ralement au format . Cela Ă©vite de passer l'identifiant d'une entitĂ© lĂ oĂč un autre Ă©tait attendu et rend les signatures plus expressives dans commands, queries, entitĂ©s, handlers et mappings.
- SĂ©curitĂ© de type : empĂȘche de mĂ©langer accidentellement les identifiants d'entitĂ©s diffĂ©rentes.
- Clarté : rend les signatures et contrats plus expressifs que des types primitifs isolés.
- Refactoring : concentre les changements de format ou de sérialisation dans le type d'ID correspondant.
- Réduction des erreurs : rend les usages incorrects visibles à la compilation chaque fois que possible.
Intégration entre services
Lorsqu'un service doit réagir à un autre, préférez les événements d'intégration, les intégrations HTTP explicites ou les données répliquées consciemment. L'accÚs direct à la base d'un autre service crée un couplage structurel, complique les migrations et rend les deploys indépendants plus risqués.
Prochaines étapes pour les services modulaires
AprÚs avoir créé un service modulaire, l'étape suivante consiste à ajouter des modules avec lino module new. Chaque module doit représenter sa propre capacité métier, avec domaine, application, persistance, API, tests et intégrations qui préservent la limite interne du service.
Créer et gérer des modules
Les modules existent uniquement Ă l'intĂ©rieur de services modulaires. Un module reprĂ©sente une limite mĂ©tier dans le mĂȘme runtime : il possĂšde son propre modĂšle de domaine, ses cas d'utilisation, sa persistance, sa surface d'API, ses Ă©vĂ©nements d'intĂ©gration, ses tests et son schema de base de donnĂ©es.
Utilisez des modules lorsque le service doit hĂ©berger plus d'une capacitĂ© mĂ©tier sans mĂ©langer entitĂ©s, rĂšgles, migrations et contrats. Dans un monolithe modulaire, tout peut s'exĂ©cuter dans le mĂȘme processus, mais les limites internes restent importantes : l'isolation rĂ©elle relĂšve du contrĂŽle des dĂ©pendances, pas seulement de la sĂ©paration des dossiers.
lino module new --service
Pendant la création, l'assistant demande :
- Service : le service modulaire qui hébergera le module. Lino n'autorise pas la création de modules dans des services simples, car ils ne possÚdent pas la structure nécessaire à l'isolation interne.
- Namespace du module : le nom technique utilisé dans les dossiers, namespaces, assemblies et projets.
- Nom d'affichage : le nom convivial du module dans les emplacements destinés à la lecture humaine.
- Strongly Typed IDs : définit si les identifiants générés dans le module utiliseront des types dédiés, généralement au format
.Id
AprĂšs confirmation, Lino ajoute le module dans le service sans faire disparaĂźtre la limite architecturale :
src/Services// âââ Host/ âââ Infrastructure/ âââ Modules/ âââ / âââ Domain/ â âââ . . .Domain.csproj âââ Application/ â âââ . . .Application.csproj âââ Infrastructure.Persistence/ â âââ . . .Infrastructure.Persistence.csproj âââ Infrastructure/ â âââ . . .Infrastructure.csproj âââ Api/ â âââ . . .Api.csproj âââ Integration.Events/ (quand la messagerie existe) â âââ . . .Integration.Events.csproj âââ Api.Contracts/ (quand la consommation HTTP typĂ©e existe) â âââ . . .Api.Contracts.csproj âââ Api.Client/ (quand la consommation HTTP typĂ©e existe) âââ . . .Api.Client.csproj tests/Services/ /Modules/ / âââ UnitTests/ â âââ Domain/ â â âââ . . .Domain.UnitTests.csproj â âââ Application/ â âââ . . .Application.UnitTests.csproj âââ IntegrationTests/ âââ . . .IntegrationTests.csproj
Responsabilités des artefacts générés
| Artefact | Responsabilité dans le module |
|---|---|
| Domain | Entités, agrégats, Value Objects, enumerations, événements de domaine, contrats de repository et invariants du module. |
| Application | Cas d'utilisation, commands, queries, handlers, validations, contrats internes d'entrée et de sortie, et orchestration des rÚgles du module. |
| Infrastructure.Persistence | DbContext, configurations Entity Framework Core, repositories concrets, Unit of Work et migrations du module. |
| Infrastructure | Implémentations techniques propres au module, adaptateurs, providers et composition des dépendances. |
| Api | Endpoints HTTP, versioning, filtres, autorisation et adaptation entre les requĂȘtes externes et les cas d'utilisation. |
| Api.Contracts | Généré lorsqu'il existe une consommation HTTP typée, généralement par des Web Apps Blazor. Contient requests, responses, DTOs, types publics et interfaces de client partagées entre l'API et le consommateur. |
| Api.Client | Généré avec les contrats lorsqu'il existe une consommation HTTP typée. Contient l'implémentation HTTP des interfaces, avec HttpClient, providers, options et helpers afin que les projets Blazor consomment les APIs générées de maniÚre cohérente et fortement typée. |
| Integration.Events | Généré lorsque le projet possÚde de la messagerie. Contient les événements d'intégration émis et consommés par d'autres modules, services ou systÚmes, en conservant le payload comme contrat explicite. |
Structure de base de données
La base de données reste liée au service, pas au module isolément. Dans un service modulaire, chaque module est représenté par son propre schema dans la base associée, avec son propre projet de persistance et ses propres migrations. Cela offre isolation et organisation sans exiger plusieurs bases physiques pour chaque module.
Isolation et indépendance entre modules
Un module ne doit pas accéder directement au DbContext, aux entités, aux repositories ou aux services internes d'un autre module. Chaque module possÚde son propre modÚle et sa propre persistance. Lorsqu'un autre module a besoin de données, utilisez une intégration explicite au lieu de traverser la frontiÚre par des détails internes d'implémentation.
Cela évite qu'un changement apparemment local casse une autre zone du systÚme. Si un module consommateur doit consulter des données qui appartiennent à un autre module, il ne doit pas dépendre de l'entité complÚte du module source. Il peut conserver une shadow entity avec les données minimales nécessaires à son propre cas d'utilisation, alimentée par intégration ou événement.
Avantages de ce découplage :
- Isolation : chaque module peut faire évoluer rÚgles, persistance et tests sans traverser les détails internes d'un autre module.
- Organisation : l'application respecte les bounded contexts et rend l'ownership explicite.
- FlexibilitĂ© : les modules peuvent ĂȘtre ajoutĂ©s, supprimĂ©s ou refactorisĂ©s avec moins d'impact sur le reste du service.
- FacilitĂ© de test : chaque module peut ĂȘtre validĂ© de maniĂšre plus indĂ©pendante, ce qui augmente la confiance dans les changements.
Communication entre modules
Utilisez des contrats explicites pour la communication. Pour les appels dans le mĂȘme runtime, une intĂ©gration interne peut exposer des contrats dans Integration.Contracts et une implĂ©mentation in-process lorsqu'elle est gĂ©nĂ©rĂ©e. Pour les appels entre runtimes, utilisez une intĂ©gration HTTP. Pour la propagation asynchrone, publiez des Ă©vĂ©nements d'intĂ©gration dans Integration.Events et utilisez l'infrastructure de messagerie/outbox lorsque la publication fait partie d'une opĂ©ration transactionnelle.
Schemas, migrations et base de données
Dans un service modulaire, les modules partagent le provider de base de données du service, mais chaque module est représenté par son propre schema et par son propre projet Infrastructure.Persistence. Les migrations sont générées par module, ce qui maintient l'évolution de la base alignée avec la limite métier.
Cette sĂ©paration permet Ă chaque module de faire Ă©voluer ses tables, seeds, index et foreign keys sans transformer la base du service en un modĂšle unique partagĂ© par tous. Le schema est une limite technique qui renforce la limite mĂ©tier, mĂȘme lorsque les modules s'exĂ©cutent dans le mĂȘme processus et utilisent la mĂȘme base physique.
Bonnes limites de module
CrĂ©ez les modules autour de capacitĂ©s mĂ©tier, pas autour de couches techniques. Un bon nom de module doit dĂ©crire une responsabilitĂ© reconnaissable par le domaine, comme une zone, un processus ou une capacitĂ© qui possĂšde ses propres rĂšgles. Ăvitez les noms gĂ©nĂ©riques comme Common, Core ou Utilities pour les rĂšgles mĂ©tier, car ils masquent l'ownership et tendent Ă devenir des dĂ©pendances trop partagĂ©es.
- Cohésion : le module doit avoir un langage, des rÚgles et des données qui changent ensemble.
- Autonomie : il doit ĂȘtre possible de tester et de faire Ă©voluer le module sans accĂ©der aux entitĂ©s internes d'un autre module.
- Contrats clairs : les donnĂ©es nĂ©cessaires hors du module doivent ĂȘtre exposĂ©es par API, intĂ©gration, Ă©vĂ©nement ou shadow entity, pas par accĂšs direct Ă la base.
- Faible couplage : si deux modules doivent modifier les mĂȘmes entitĂ©s en permanence, la limite doit probablement ĂȘtre réévaluĂ©e.
Une fois les modules créés, les prochains sujets de la documentation montrent comment modéliser entités, Value Objects, enumerations, commands, queries, APIs, événements, intégrations et migrations à l'intérieur de ces limites.
