As we have seen in the previous chapters the UI is backed by objects in the server side. We have also seen that, unless we define them as singletons, those server-side can are ephimeral and they are created and die with every request as the nature of Mateu is stateless in the server side.
That stateless nature is mandatory if we want to live in the micro services world, were pods are created and destroyed at any moment and load is balanced among multiple pods, so we do not know who will handle our requests. Same for serverless.
The server-side object life-cycle
So, the life cycle of the objects in the server side is as follows:
- Mateu receives a call from the browser for performing an action, for a component
- Mateu gets an instance of the object with the class corresponding to that component
- Mateu hydrates the object with the values from the component state
- If the HasPostHydratationMethod interface is implemented, the postConstruct method is called
- The required method in the object is called
- The return is mapped to a Mateu dto
- The dto multilanguage contents (labels) is translated
- The dto is sent back to the browser and rendered
1. Http request handling
The controllers generated by Mateu handle the request and call the appropriate use case.
2. Object instantiation
Please notice that Mateu first looks for a bean from the underlying framework (Springboot, Quarkus, Micronaut, Helidon). If the application context does not return a bean, then Mateu creates an instance which will live only for this request.
You can provide you own factories by providing beans which implement the InstanceFactory interface.
When creating an instance, Mateu first looks for a constructor which matches the values in the component state. This is usually the case for Java records. If that constructor does not exist, then the default constructor with no parameters is used.
Please notice that there is some more logic here in order to support routing and declarative apps. This additional logic is explained at the end of this chapter.
3. Hydratation
This only happens if the object implements the Hydratable interface or if Mateu has not found a constructor with parameters matching the state values. Setters are used if available.
4. HasPostHydratationMethod
If the object implements the HasPostHydrationMethod interface, then the onHydrated method is called.
This is usually used to read parameters or headers from the http request and fill the object fields, e.g. after a database or web service call, specially if the object is a bean whose initialization is out of our control.
5. Method is called
If the object implements the HandlesActions interface then the handleAction method is called, otherwise the method with the same name as the action id will be called.
You can override the default action handling by providing beans implementing the ActionRunner interface.
6. Map result to a DTO
In case null is returned then the object itself will be serialised as the new component state.
You can override the default serialization by providing beans implementing the UIIncrementMapper interface.
7. Translate
Remember i18n happens in the server side. There is no client-side i18n in Mateu.
Mateu will by default use the usual resource bundles in java. You can override the default implementation by providing a bean implementing the Translator interface.
8. Send dtos to the frontend
This will be the http response or a flow of objects, if you are using server sent events (SSE).
Instantiation explanation extended
It’s clear that we instantiate an object at the beginning of the flow but, how do we know which object to instantiate?
Well, for what is of interest here, our inputs contain:
- the route
- the part of that route which has already been consumed
- an action id
- optionally the server-side type
- optionally an application server-side type
So, our pseudocode for arriving to the target instance is the following:
if (the server-side-type is informed) {
return it
}
if (the app server-side type is informed) {
return resolve menu in app; // find option in menu with name == action or remaining route
}
return guess from route
Guess from route logic:
if (there is an app matching a part of the route) {
return the app class and the consumed part of the route;
}
if (there is a matching route){ //a class annotated with @Route
if (the class implements RouteHandler) {
instantiate and return the route handling result
}
return the class matching the route;
}
return the class matching the UI; // the class annotated with @MateuUI mathing the base route