This page organizes the management method of business logic introduced in the new project. How to structure and use related classes in the situation that "you don't need a huge DI container for Web API like Spring"? Is the focus point.
This project was a simple common library that returned the result when an argument was passed to a single method that became the entry point. From the entry point, business logic associated with multiple entities is defined (a so-called MVC-like architecture).
When changes are made to the business logic, we considered this time on the assumption that the number of services that have business logic instead of the entry point will increase or change.
The service layer class referenced from the entry point is 4 or 5 at most, so you can instantiate it directly ...
I considered it from four perspectives:
I decided to prepare a simple DI container. It's an image of having a class with such a structure between a factory method, a singleton, and a service locator mediate. The factory class itself should be a single instance, and services should be retrieved from this single instance.
Also, make sure that business logic updates are only affected through the factory. When business logic was added, I thought it might be possible to add an instantiation method to the factory.
Then, the upper layer class that depends on the service generates the service from the factory and uses it. Annotation-based DI like Spring and Java EE seems to be a little overkill in this project, so I did not adopt it in this project. As a result of various investigations, I adopted a pattern called DYDI (Do it Yourself Dependency Injection), which is easy to prepare by myself and seems to be suitable for a compact project like this one.
It consists of three classes: a class that generates logic, an interface / implementation of logic, and a caller.
First, prepare a factory class that returns an instance with logic.
ServiceFactory.java
public class ServiceFactory {
/**
*Factory-specific single instance.
*/
private static final ServiceFactory serviceFactory = new ServiceFactory();
private ServiceFactory() {}
public static ServiceFactory getInstance() { return serviceFactory; }
/**
*Returns the substance of the logic.
*/
public TargetLogic getTargetLogic() {
return new TargetLogicImpl();
}
Define the interface and logic returned from this factory. This is a quick definition as it can be flexibly defined according to the use case.
TargetLogic.java
public interface TargetLogic {
void consume();
}
TargetLogicImpl.java
public interface TargetLogicImpl implements TargetLogic {
void consume() {
//Something processing
}
}
Finally, we will call the logic.
Application.java
public class Application {
private final TargetLogic targetLogic = ServiceFactory.getInstance().getTargetLogic();
}
You can now get the logic through the factory in the ʻApplication` class.
As I read at the beginning, the application was small and the project structure was simple, so the dependency management method was just right. Even if there is a logic change in the future, it seems that it can be organized simply because it is enough to add a method to the factory. When should you create an instance? Shouldn't it be made? It was a side effect that deepened my thinking about.
On the other hand, if your project is a little bigger or has more layers, it's more straightforward to rely on libraries like Spring and Guice. To be honest, there may be more projects that are hard to work with.