[JAVA] Try Dependency Inversion Principle with Multiple Spring Boot Projects

what

--I read CleanArchitecture and wanted to see what happens if I implement some scenarios in Spring Boot.

map

-(Hereafter, it is a simplified rough map, and the book may not say or say such a thing) --A component is a collection of related functions. --Components have a gradation from detail to essence --Details can be replaced. Where to save the data (DB, file, cloud storage) and how to display it (WEB, console, PDF, etc ..) are detailed. --In essence, it is an entity or a use case that uses an entity. --Something has the most important business rules and data. --Use cases have application-specific business rules. (Define how to use the entity, regardless of details) --The farther the distance from the input / output, the higher the level (= the higher the essence. The higher the level of the entity). --Higher level components should not be aware of lower level components. (Something is use case independent. Use case is not detailed) --If the upper level becomes aware of the lower level, it can be replaced by a dependency on abstraction by using an interface in the middle. (Dependency inversion principle) --Example) In the use case, when "save the entity", it should not appear in the use case implementation that it is "in MySQL". By sandwiching the interface, "save it somewhere appropriate" --The Dependency Inversion Principle itself does not mention how to separate components. There are three types of separation methods. (Source level, Deployment level, Service level) --The source level is the class structure in a single project. There are separate jars at the deployment level. At the service level, the quarantine level rises, and communication is not possible without network communication. --Of these, try deploy-level disconnection with multiple Spring Boot projects and see what happens to the pom.xml dependencies. ――The higher the level of separation, the higher the development and management costs. (For example, how to manage and disseminate the workable version when separating jars?) The monolithic configuration is not absolutely bad. (However, if you do not keep in mind the boundaries, you can no longer separate when the need arises.) ――One example of the merit of separation is that you can switch plugins and details without compiling.

Project to create

artifact-id Namespace Contents
my-business com.example.demo.business Entity, use case, repository interface
my-repository1 com.example.demo.repository Has the substance of the repository(Example: for mysql)
my-repository2 com.example.demo.repository Has the substance of the repository(Example: For files)
my-runner com.example.demo.runner Batch processing start main

--In this sample, the namespaces of my-repository 1 and 2 are the same.

Dependencies in pom.xml

modules.png

--my-runner is using my-repository1. my-repository2 is unused.

Source sample (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);
}

Source sample (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);		
	}

}

Source sample (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);		
	}

}

Source sample (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]);
    }

}

Ingenuity to move these with Spring Boot, etc.

--scanBasePackages lists all namespaces. --Except for my-runner, which is the startup point, I deleted the following from pom.xml. With this, I was angry that there was no main in the project at the time of mvn install

pom.xml


	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

starting method

--Create a jar for each project except my-runner. `mvn install -DskipTests = true` --Start my-runner.

init.sh


$ mvn spring-boot:run -Dspring-boot.run.arguments="hello"
...
running runner...
save to mysql.hello

Impressions of making it. Merit edition

--By making other projects (my-runner, my-repository1) unavailable from the essence (my-business), you can enforce the "essence must not depend on details" rule by compiling. --Repositories can be switched by simply rewriting pom.xml in my-runnner and without compiling.

Impressions of making it. Disadvantages

--Development efficiency drops. When it doesn't work well, there are various possibilities, so it's difficult to isolate. It gets even harder when you start versioning jars.

What I didn't understand

--For scanBasePackages, if there are multiple detailed modules that may be switched in the future, should I list them if I want to switch without compiling? --I expected the IDE to take care of me without having to do `` `mvn install``` every time I modified the source under development, but it didn't work. It seems that setting is necessary somewhere.

Future development

--What if you want to separate the use case and the entity into separate jars? ――How can I remove the Spring annotations attached to the use case? ――What happens if you introduce the use case output port (I), use case input port (I), and use case interactor that appear in Chapter 22 of the book?

Recommended Posts

Try Dependency Inversion Principle with Multiple Spring Boot Projects
Try using Spring Boot with VS Code
Try LDAP authentication with Spring Security (Spring Boot) + OpenLDAP
Try to implement login function with Spring Boot
Try to automate migration with Spring Boot Flyway
Download with Spring Boot
Try using DI container with Laravel and Spring Boot
Try using OpenID Connect with Keycloak (Spring Boot application)
Try Spring Boot from 0 to 100.
Generate barcode with Spring Boot
Hello World with Spring Boot
Implement GraphQL with Spring Boot
Get started with Spring boot
Hello World with Spring Boot!
Run LIFF with Spring Boot
SNS login with Spring Boot
File upload with Spring Boot
Spring Boot starting with copy
Spring Boot starting with Docker
Hello World with Spring Boot
Set cookies with Spring Boot
Use Spring JDBC with Spring Boot
Add module with Spring Boot
Getting Started with Spring Boot
Try using Spring Boot Security
Try Spring Boot on Mac
Create microservices with Spring Boot
Send email with spring boot
Try hitting the zip code search API with Spring Boot
Use Basic Authentication with Spring Boot
gRPC on Spring Boot with grpc-spring-boot-starter
Create an app with Spring Boot 2
Hot deploy with Spring Boot development
Database linkage with doma2 (Spring boot)
Spring Boot programming with VS Code
Until "Hello World" with Spring Boot
Inquiry application creation with Spring Boot
Part 1: Try using OAuth 2.0 Login supported by Spring Security 5 with Spring Boot
Get validation results with Spring Boot
(Intellij) Hello World with Spring Boot
Create an app with Spring Boot
Google Cloud Platform with Spring Boot 2.0.0
Check date correlation with Spring Boot
I tried GraphQL with Spring Boot
[Java] LINE integration with Spring Boot
Try running Spring Boot on Kubernetes
[Spring] Autowired multiple beans. (With bonus)
Beginning with Spring Boot 0. Use Spring CLI
I tried Flyway with Spring Boot
Message cooperation started with Spring Boot
Spring Boot gradle build with Docker
How to read Body of Request multiple times with Spring Boot + Spring Security
Processing at application startup with Spring Boot
[Java] [Spring] Spring Boot Dependency injection mysterious hamarineta
Hello World with Eclipse + Spring Boot + Maven
Send regular notifications with LineNotify + Spring Boot
Perform transaction confirmation test with Spring Boot
HTTPS with Spring Boot and Let's Encrypt
Start web application development with Spring Boot
Launch Nginx + Spring Boot application with docker-compose
I tried Lazy Initialization with Spring Boot 2.2.0