
I want to tell the Habr Java community about a small, but very useful (personal experience) framework called
Green-forest . This framework can be used both independently and in the context of JEE or Spring.
How it can be used to simplify the application code will be found under the cut.
')
In the beginning we consider the problem for the solution of which Green-forest is intended.
Problem
Very often, the architecture of a large project has the following form:
- Presentation layer
- Business layer
- Data layer
In this case, the business layer and the data layer consist of classes of services that provide the public API to the outside world.
An example of such a service:
public interface UserService { User createUser(Object someData); void activateUser(long id); User updateUser(User updatedUser); void blockUser(long id); void unblockUser(long id); }
As the application grows, the number of methods in such services grows, which means the cumbersome implementation-class increases:
public class UserServiceImpl implements UserService {
As a result, classes appear that contain dozens of methods and 1000 lines of code. Search and maintain code in such classes becomes difficult and dreary.
Classic solution
The obvious solution seems to be the division of one large service into many smaller ones:
public interface UserBasicService { User createUser(Object someData); void activateUser(long id); User updateUser(User updatedUser); } public interface UserModerationService { void blockUser(long id); void unblockUser(long id); }
However, besides simplicity, this approach has several disadvantages:
The difficulty of uniform separation : it is difficult to divide the service into equal parts. In one new service, 3 methods remain, in the other - 10.
The ambiguity of separation : it is difficult to determine which child service to transfer a specific method. As a result, methods are transferred illogically and the API is complicated to understand.
Elimination of symptoms, but not the reasons: in fact, we did not solve the problem, because with the growth of methods in new services, they also risk turning into bulky 1000 line implementations.
Let's try to solve the problem of bulky service in a new way.
Solution through atomic functions
Since our simple division of the service into several similar ones does not provide a complete solution to the problem, let us ask ourselves, can we divide it into more atomic entities? Of course we can! After all, the purpose of any service is its methods, which means that by creating a lot of individual methods, we will get rid of the shortcomings voiced above:
There are no problems of uneven and ambiguous separation : the set of methods is divided evenly and unequivocally by its definition.
The growth problem has been solved: when new methods appear, we simply create new classes without increasing the size of the old ones.
Implementation of the solution in Green-forest
The Green-forest Framework uses the Action-Handler architecture, illustrated by the diagram:

Consider the components of this scheme:
Action
The Action class represents an atomic "method" that can be "called." Inside the class contains input and output data:
Handler
The Handler class contains a specific implementation for this Action type:
@Mapping(SomeAction.class) public class SomeHandler extends Handler<SomeAction>{ @Inject SomeService service; public void invoke(SomeAction action) throws Exception { String input = action.getInput(); Integer result = service.doSomeWork(input); action.setOutput(result); } }
Framework
The Engine Framework object provides the link between Action and Handler objects:
Usage example
So, we have considered the key classes of the framework. Using them, we will refactor the example with our UserService service.
At the beginning, we will inherit the UserService from the ActionService service interface:
import com.gf.service.ActionService; public interface UserService extends ActionService {
Next, create a set of Action classes:
import com.gf.Action; public class CreateUser extends Action<Object, User> { public CreateUser(Object someData){ this(someData); } } public class ActivateUser extends Action<Long, Void> { public ActivateUser(Long id){ this(id); } }
It remains to create handlers for these classes. For example:
@Mapping(CreateUser.class) public class CreateUserHandler extends Handler<CreateUser>{ public void invoke(CreateUser action) throws Exception { Object input = action.getInput(); User user = ... action.setOutput(user); } }
An example of the final implementation of the UserServiceImpl implementation:
import com.gf.Action; import com.gf.core.Engine; public class UserServiceImpl implements UserService { Engine engine; public UserServiceImpl(){ engine = new Engine();
As a result, we divided the large UserService interface into a set of Action classes, and the implementation of the UserServiceImpl into a set of Handler classes.
Conclusions and references
We considered the problem of concentrating logic in one class and ways of solving, including the division into atomic class methods. Next, we met the Green-forest Framework, which implements the latter approach using the Action-Handler architecture. Using Green-forest, you can simplify and structure the code of your application.
Enjoy your work, colleagues!
References:
Green-forest FrameworkDocumentation