[JAVA] Essayez la loi de l'inversion des dépendances avec plusieurs projets Spring Boot
quoi
- J'ai lu Clean Architecture et je voulais voir ce qui se passe lorsque j'implémente certains scénarios avec Spring Boot.
carte
- (Ce qui suit est une carte approximative simplifiée, et le livre peut ne pas dire ou dire une telle chose)
--Un composant est une collection de fonctions associées.
- Les composants ont une gradation du détail à l'essence
- Les détails peuvent être remplacés. Où enregistrer les données (DB, fichier, stockage cloud) et comment les afficher (WEB, console, PDF, etc.) sont détaillés.
- En substance, il s'agit d'une entité ou d'un cas d'utilisation qui utilise une entité.
- L'entité dispose des règles et des données commerciales les plus importantes.
- Les cas d'utilisation ont des règles métier spécifiques à l'application. (Définissez comment utiliser l'entité, quels que soient les détails)
- Plus le niveau est éloigné de l'entrée / sortie, plus le niveau est élevé (= plus l'essence est élevée. Plus le niveau de l'entité est élevé).
- Les composants de niveau supérieur ne doivent pas être conscients des composants de niveau inférieur. (L'entité est indépendante du cas d'utilisation, le cas d'utilisation n'est pas détaillé)
- Si le niveau supérieur prend conscience du niveau inférieur, il peut être remplacé par une dépendance à l'abstraction en utilisant une interface au milieu. (La loi du renversement de la dépendance)
--Exemple) Lorsque "enregistrer une entité" dans un cas d'utilisation, le fait qu'elle soit "dans MySQL" ne doit pas apparaître dans l'implémentation du cas d'utilisation. En intercalant l'interface, «enregistrez-la quelque part»
- La loi de l'inversion de dépendance elle-même ne mentionne pas comment séparer les composants. Il existe trois types de méthodes de séparation. (Niveau source, niveau de déploiement, niveau de service)
- Le niveau source est la structure de classe dans un seul projet. Il existe des fichiers JAR séparés au niveau du déploiement. Au niveau du service, le niveau d'isolement augmente et la communication n'est pas possible sans communication réseau.
- Parmi ceux-ci, essayez la déconnexion au niveau du déploiement avec plusieurs projets Spring Boot pour voir ce qu'il advient des dépendances pom.xml.
―― Plus le niveau de séparation est élevé, plus les coûts de développement et de gestion sont élevés. (Par exemple, comment gérez-vous et diffusez-vous la version opérable lorsque vous divisez le pot?) La configuration monosilique n'est pas absolument mauvaise. (Cependant, si vous ne gardez pas à l'esprit les limites, vous ne pouvez plus vous séparer lorsque le besoin s'en fait sentir.)
«Un exemple du mérite de la séparation est que vous pouvez changer de fiche et de détails sans compiler.
Projet à créer
artifact-id |
Espace de nom |
Contenu |
my-business |
com.example.demo.business |
Entité, cas d'utilisation, interface de référentiel |
my-repository1 |
com.example.demo.repository |
A la substance du référentiel(Exemple: pour mysql) |
my-repository2 |
com.example.demo.repository |
A la substance du référentiel(Exemple: pour les fichiers) |
my-runner |
com.example.demo.runner |
Le traitement par lots démarre main |
- Dans cet exemple, les espaces de noms de my-repository 1 et 2 sont identiques.
Dépendances dans pom.xml
--my-runner utilise my-repository1. my-repository2 n'est pas utilisé.
Échantillon source (my-business)
SomeEntity.java
package com.example.demo.business;
public class SomeEntity {
public int id;
public String name;
}
SomeUseCase.java
package com.example.demo.business;
@Component
public class SomeUseCase {
@Autowired
private MyRepositoryInterface repo;
public void doSomething(String name) {
SomeEntity entity = new SomeEntity();
entity.name = name;
repo.save(entity);
}
}
MyRepositoryInterface.java
package com.example.demo.business;
public interface MyRepositoryInterface {
public void save(SomeEntity someEntity);
}
Échantillon source (my-repository1)
MyRepositoryImpl1.java
package com.example.demo.repository;
import org.springframework.stereotype.Component;
import com.example.demo.business.MyRepositoryInterface;
import com.example.demo.business.SomeEntity;
@Component
public class MyRepositoryImpl1 implements MyRepositoryInterface {
public void save(SomeEntity someEntity) {
System.out.println("save to mysql." + someEntity.name);
}
}
Échantillon source (my-repository2)
MyRepositoryImpl2.java
package com.example.demo.repository;
import org.springframework.stereotype.Component;
import com.example.demo.business.MyRepositoryInterface;
import com.example.demo.business.SomeEntity;
@Component
public class MyRepositoryImpl2 implements MyRepositoryInterface {
public void save(SomeEntity someEntity) {
System.out.println("save to file." + someEntity.name);
}
}
Échantillon source (my-runner)
MyRunnerApplication.java
package com.example.demo.runner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages={"com.example.demo.runner, com.example.demo.business, com.example.demo.repository"})
public class MyRunnerApplication {
public static void main(String[] args) {
SpringApplication.run(MyRunnerApplication.class, args);
}
}
MyRunner.java
package com.example.demo.runner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import com.example.demo.business.SomeUseCase;
@Component
public class MyRunner implements CommandLineRunner{
@Autowired
private SomeUseCase someUseCase;
@Override
public void run(String... args) throws Exception {
System.out.println("running runner...");
someUseCase.doSomething(args[0]);
}
}
Ingéniosité pour les déplacer avec Spring Boot
--scanBasePackages répertorie tous les espaces de noms.
- Sauf pour my-runner, qui est le point de démarrage, j'ai supprimé ce qui suit de pom.xml. Avec cela, j'étais en colère qu'il n'y avait pas de main dans le projet au moment de `` mvn install```
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
méthode de départ
--Créez un pot pour chaque projet sauf my-runner. `mvn install -DskipTests = true
`
init.sh
$ mvn spring-boot:run -Dspring-boot.run.arguments="hello"
...
running runner...
save to mysql.hello
Impressions de le faire. Édition Merit
- En rendant l'essence (mon-entreprise) indisponible pour d'autres projets (mon-coureur, mon-référentiel1), vous pouvez appliquer la règle "l'essentiel ne doit pas dépendre des détails" en compilant.
- Les référentiels peuvent être changés en réécrivant simplement le pom.xml de my-runnner sans compiler.
Impressions de le faire. Désavantages
- L'efficacité du développement diminue. Quand ça ne marche pas bien, il y a plusieurs possibilités, donc c'est difficile à isoler. Cela devient encore plus difficile si vous commencez à gérer la version de jar.
Ce que je n'ai pas compris
- Pour scanBasePackages, s'il y a plusieurs modules détaillés qui peuvent être commutés à l'avenir, dois-je les lister si je veux changer sans compiler?
- Je m'attendais à ce que l'EDI prenne soin de moi sans avoir à faire
mvn install ''
chaque fois que je modifiais la source en cours de développement, mais cela ne fonctionnait pas. Il semble que le réglage soit nécessaire quelque part.
Développement futur
- Que faire si vous souhaitez séparer le cas d'utilisation et l'entité dans des fichiers JAR séparés?
- Comment puis-je me débarrasser des annotations Spring sur mon cas d'utilisation?
―― Que se passe-t-il si vous introduisez le port de sortie de cas d'utilisation (I), le port d'entrée de cas d'utilisation (I) et l'interacteur de cas d'utilisation qui apparaissent au chapitre 22 du livre?