-
Notifications
You must be signed in to change notification settings - Fork 0
Monolithic to Microservices using FrontierAPI
On a typical monolithic project we have the logic layer calling different repositories to persist some data, this logic layer is also referred as Service layer. As an example lets consider this monolithic project, that models a simple use case of a Pet Owner managing his Pet, in which we can create an Owner and assign a new Pet to him.
Monolithic example
@Service
public class OwnerService {
@Autowired
private OwnerRepository ownerRepository;
@Autowired
private PetRepository petRepository;
public void saveOwnerWithPet(Owner owner, Pet pet) {
pet.setOwner(owner);
ownerRepository.save(owner);
petRepository.save(pet);
}
}
public interface PetRepository extends CrudRepository<Pet, Long> {
List<Pet> findByOwner(Owner owner);
}
public interface OwnerRepository extends CrudRepository<Owner, Long> {
}
Now lets consider how we can migrate the above code to Microservices, using the FrontierAPI.
@Service
public class OwnerService {
@Autowired
private OwnerRepository ownerRepository;
@Autowired
private PetRepository petRepository;
public void saveOwnerWithPet(Owner owner, Pet pet) {
pet.setOwner(owner);
ownerRepository.save(owner);
petRepository.save(pet);
}
}
@FrontierConsumerRepository(entity=Pet.class)
public interface PetRepository {
/* Spring supports CrudRepository, PagingAndSortingRepository, JpaRepository implementations.
* FrontierAPI has no plan on supporting such implementations, so all Spring repository implementations must be defined here.
*/
void save(Pet pet);
List<Pet> findByOwner(Owner owner);
}
public interface OwnerRepository extends CrudRepository<Owner, Long> {
}
@FrontierProviderRepository
public interface PetRepository extends CrudRepository<Pet, Long> {
@FrontierProperties(guarantee="synchronous", isCacheable=false, maskedFaults=false)
List<Pet> findByOwner(Owner owner);
}
In this example several Frontier annotations were used. Frontier annotations can be split into two major types,Consumer and Provider.
Provider annotations are used to expose resources with certain guarantees, those resources will be registered under the FrontierAPI. The annotation will be caught by Spring BeanPostProcessor in run time, making the expression inside the annotation not compile time safe. After being caught by the BeanPostProcessor, the class will be consumed with respect to the following several phases:
- Register phase
- Wake phase
Register annotations are used to query FrontierAPI and contruct a client for the exposed resource based on the response configuration from the FrontierAPI. All of the Frontier configuration will be interpreted to provide specific logic. The data relevant here is:
- Class canonical path
- Service name
- Methods (name, return Type, variables Type)
- Annotation attributes: Guarantee, isCacheable and maskedFaults
Based on the Return type and arguments, a serializable DTO will be created both for sending and receiving purposes, in each case. The guarantee will determine the sending protocol, and what type of Bean is to be created.
- Synchronous - @RestController bean
- Asynchronous|Best-effort - RabbitMQ with different set of configuration parameters Register all the configurations to be initialised in the next stage.
This phase will pick the generated configuration, based on the Register phase, and will register on the Frontier API what endpoints are being exposed, and to what purpose. This will be useful so that the Consumer side can query the Frontier API for exposed configurations.