Skip to content

Monolithic to Microservices using FrontierAPI

Diogo Santos edited this page Oct 15, 2019 · 9 revisions

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.

Owner Microservice

@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> {
}

Pet Microservice

@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

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:

  1. Register phase
  2. Wake phase

Register 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.

Wake phase

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.

Clone this wiki locally