--I read CleanArchitecture and wanted to see what happens if I implement some scenarios in Spring Boot.
-(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.
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.
--my-runner is using my-repository1. my-repository2 is unused.
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);
}
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);
}
}
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);
}
}
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]);
}
}
--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>
--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
--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.
--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.
--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.
--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