# 4Building your First RESTful CRUD API.
## Part 4: Creating the Service Layer
For the Reseravation Management System, we know that one of the key requirements the system needs to handle the following functionality:
- ***C***reate a new reservation, with a unique `reservationId`.
To create the unique `reservationId`, the business has told us that the requirement is as follows:
- A Reservation ID should take the first and last characters of the customer's first and last name, along with a 5 digit number e.g. `Joe Bloggs => 'jebs-12345'`.
- If the reservation has less than 6 poeple, then set the duration to be for at least 1 hour, otherwise set the duration for at least 2 hours.
- These requirements make a great candidate for.... a ***service layer!!***
- ***R***etrieve a reservation
- ***U***pdate a reservation
- ***D***elete a reservation
The ***Service layer*** - This is where the magic happens! 😉 Here, you will keep all the custom business logic that is specific to your domain / application, such as creating a unique ID based on your organisational requirements. You can also have custom ways of handling errors or even make calls out to multiple other services.
**Lets stick to some best practises, shall we?**
If we want to stick to best practises (which we obviously should), when createing any layer within any project, we should always program to an interface. The reason you want to do this is so that you are tying yourself to the behaviour of what you want, rather than to the implementation details == **loose coupling between layers**. If you need to change an implementation, it will not introduce any breaking changes upstream! Less problems == happy devs 😎.
So lets start with that
## Creating the ReservationService Interface
- Create a new package called `service` in `com.rms.reservationservice.service`.
- Create an interface called `ReservationService`. This will contain all the methods that we want any implementation of the `ReservationService` to have.
```
import com.rms.reservationservice.model.Reservation;
public interface ReservationService {
Reservation saveReservation(Reservation reservation); //1
Reservation getReservationById(String reservationId); //2
Reservation updateReservation(String reservationId, Reservation reservation); //3
void deleteReservation(String reservationId); //4
}
```
*I have put numbers next to each method, as they will be discussed individually in turn 🙌*.
***Phew, that was easy! what now??***
## Creating the ReservationServiceImpl implementation class
Now that we have an interface to work with, we need an implementation class that will keep our business logic, and even perform operations like calling our repository layer, so that we can interact with the database easily.
- Create a class called `ReservationServiceImpl` within the `service` package. This will implement all the methods from the `ReservationService` interface.
```
package com.rms.reservationservice.service;
import com.rms.reservationservice.model.Reservation;
import com.rms.reservationservice.repository.ReservationRepository;
import org.springframework.stereotype.Service;
@Service
public class ReservationServiceImpl implements ReservationService {
private final ReservationRepository reservationRepository;
public ReservationServiceImpl(ReservationRepository reservationRepository) {
this.reservationRepository = reservationRepository;
}
@Override
public Reservation saveReservation(Reservation reservation) { return null; }
@Override
public Reservation getReservationById(String reservationId) {
return null;
}
@Override
public Reservation updateReservation(String reservationId, Reservation reservation) {
return null;
}
@Override
public void deleteReservation(String reservationId) {
}
}
```
- ***@Service*** - Here, we are telling Spring that this class is a Spring Component(aka Spring Bean), just like when we annotated the ReservationRepository with the`@Repository` annotation. By doing this, Spring will create an instance of this object for us, so that we can start to make use of it wherever we like in our service.
- As this service layer is going to be interacting with the repository layer (`ReservationRepository`), and since `ReservationRepository` was annotated with `@Repository`, we can `inject` the repository dependency directly into our class and start making use of it without needing to create the instance ourselves!
- Though I will make post on this, this concept of injecting a dependency from one place to another is literally called ***DEPENDENCY INJECTION*** or ***autowiring*** in Spring lingo.
If you got stuck at any point during this, please checkout branch [part-4-creating-service-layer](https://github.com/csapty12/restaurantManagementSystem/tree/part-4-creating-service-layer/reservationservice) and copare your code against mine.
Lets start to break down each of the `ReservationServiceImpl` methods and start to provide the appropriate functionality for this service.
Once we have set up the ***happy path*** for all CRUD operations, then we will go through the ***unhappy path*** scenarios, and modify the methods to account for any errors / exceptions that may occur when performing actions on a database or within the service.
### The saveReservation method - ***C***reate
To save a new reservation in our database, the client needs to provide all the neccessary information to the service, so that we can generate an ID, attach it to the reservation, and store this in the database.
#### what needs to be done?
1. Create some utility "thing" that will let us generate ID's based on the above requirements
- create a new package called `utils` in `com.rms.reservationservice`
- Create a class called `IdGenerator`
```
package com.rms.reservationservice.utils;
import java.util.concurrent.ThreadLocalRandom;
public class IdGenerator {
private static final int MIN_VALUE = 10000;
private static final int MAX_VALUE = 99999;
public static String generateId(String firstName, String lastName) {
long randomNum = ThreadLocalRandom.current().nextInt(MIN_VALUE, MAX_VALUE + 1);
return getCharacters(firstName) + getCharacters(lastName) + "-" + randomNum;
}
private static String getCharacters(String name) {
String[] nameSplit = name.split("");
return nameSplit[0].concat(nameSplit[nameSplit.length - 1]).toUpperCase();
}
}
// Joe Bloggs => jebs-12345
```
(***There probably is a better way of implementing this, but it's for demonstration, not production*** 😉)
2. Convert the Reservation object (DTO) passed into the method into a `ReservationEntity` object (DAO) and attach an ID to it.
- We are going to create a helper method called `from()` within our `ReservationEntity` class. This will help converting the Reservation object to the ReservationEnity object.
```
import com.rms.reservationservice.model.Reservation;
//...annotations we created before
public class ReservationEntity {
//...fields that we created before e.g id, firstName etc
public static ReservationEntity from(Reservation reservation) {
return ReservationEntity.builder()
.id(reservation.getId())
.firstName(reservation.getFirstName())
.lastName(reservation.getLastName())
.reservationTime(reservation.getReservationTime())
.numberOfGuests(reservation.getNumberOfGuests())
.duration(reservation.getDuration())
.build();
}
}
```
We know that since once we save the record in the database, we are going to be returned another `ReservationEntity` object. This will need to be converted back into a `Reservation` DTO so that we can continue to use this Reservation DTO object around our application! Let's go and make that quickly.
3. Convert the response back into a `Reservation` DTO from the `ReservationEntity` DAO. (remember, the entity objects **ARE ONLY USED FOR DB INTERACTIONS, NOTHING ELSE**). Just like how we created a helper method in `ReservationEntity`, we can follow a similar pattern in the Reservation class and have a converter in here.
```
import com.rms.reservationservice.entity.ReservationEntity;
//...annoations and imports
public class Reservation {
//... fields such as id, firstName etc.
public static Reservation from(ReservationEntity reservationEntity) {
return Reservation.builder()
.id(reservationEntity.getId())
.firstName(reservationEntity.getFirstName())
.lastName(reservationEntity.getLastName())
.reservationTime(reservationEntity.getReservationTime())
.numberOfGuests(reservationEntity.getNumberOfGuests())
.duration(reservationEntity.getDuration())
.build();
}
}
```
*You could put the converter in a different Converters class or something if you did prefer that... entirely up to you!*
4. Now, we are in a position where we can start to implement the saveReservation method. Remember the two requirements are to generate an ID and set the duration of their visit based on the given number of people
```
private static final int LARGE_GROUP_OF_GUESTS = 6;
private static final int ONE_HOUR = 1;
private static final int TWO_HOURS = 2;
@Override
public Reservation saveReservation(Reservation reservation) {
Reservation.ReservationBuilder reservationBuilder = reservation.toBuilder();
if (reservation.getId() == null) {
reservationBuilder.id(generateId(reservation.getFirstName(), reservation.getLastName()));
}
if (reservation.getNumberOfGuests() < LARGE_GROUP_OF_GUESTS) {
reservationBuilder.duration(ONE_HOUR);
} else {
reservationBuilder.duration(TWO_HOURS);
}
Reservation updatedReservation = reservationBuilder.build();
ReservationEntity reservationEntity = ReservationEntity.from(updatedReservation);
ReservationEntity savedEntity = reservationRepository.save(reservationEntity);
return Reservation.from(savedEntity);
}
```
*Woah*😮! this is the most code we have written!
Well, like I said, in the service layer, this is where we keep all our business logic. In this case, before we save a new record, we need to do some checks:
- if a reservationId does not exist (which it will not if its a new reservation), then generate a new one.
- if its a group booking of less than 6, then customers can only stay for 1 hour, otherwise 2 hours.
- This is all custom logic specific to us!
Once these checks are done, we convert the `Reservation` DTO object into a `ReservationEntity` DAO object, and then call the `reservationRepository.save()` method. Notice we never implemented the `save()` method ourselves, as we let Spring JPA handle that for us!
Finally, once the record has been saved into the database successfully, we convert the response back into a `Reservation` DTO so that we can continue to use this in our app as we want.
**Right, that was a lot of information to take in! Go have a cup of ☕️ because you sure deserve it!**
If you did get stuck at any point, please checkout branch [part-4-implementing-saveReservation](https://github.com/csapty12/restaurantManagementSystem/tree/part-4-implementing-saveReservation) and compare your code against mine.
#### Extra Details that you may want to know
- I am using this `toBuilder()` method (from `@Builder` in the `Reservation` class) - this will create a shallow copy of the `Reservation` object, and then we can add any missing fields as we like. For example, as the `id` will not exist on the original `Reservation` object, we need a way to add the `id` field onto the `Reservation` object. Similarly, this is also performed for the `duration` field, as this is something we want to determine ourselves.
- Then at the end, we call `reservationBuilder.build()` which will create a new `Reservation` object (`updatedReservation`) which has all the unchanged and changed values in it.
- Now, I could have easily used setters for this, but by using a `setId()` or `setDuration()` method, you are changing the original state of the `Reservation` object, which is something we want to ***avoid*** as much as possible.
- It helps to reduce any side effects in your code, it makes your code more thread safe, and much easier to reason about as your code becomes more deterministic. I will create a post on this later to go into this in more detail. Generally speaking, ***immutability == GOOD***.
### Implementing the getReservationById method
For the happy path - Based on the `reservationId` provided, go to the database and find the record with that `reservationId`. Return back the `ReservationEntity` object, convert it back to the Reservation DTO object and return it.
```
@Override
public Reservation getReservationById(String reservationId) {
return reservationRepository.findById(reservationId)
.map(Reservation::from)
.get();
}
```
Fortunately, retrival from the database is a pretty simple process. You pass in the reservationId e.g. `JEBS-12345`, call the `reservationRepository.findById()` method. This returns back an `Optional<ReservationEntity>` object. Since we get back an optional, we can use the Higher Order Function `.map()` to map each of the values from the `ReservationEntity` DAO to the `Reservation` DTO and return back the Reservation DTO object.
If you did get stuck at any point, please checkout branch [part-4-implementing-getReservationById](https://github.com/csapty12/restaurantManagementSystem/tree/part-4-implementing-getReservationById) and compare your code against mine.
**Okay cool, we are half way there! Only update and delete left to go!**
### Implementing the updateReservation method
To update the reservation, the client needs to provide the `reservationID` to update, as well as the information as the changes to the reservation.
Since the CrudRepository does not have an "update" method, all we need to do is override the existing object in the database given the reservationId passed in with the new `Reservation` object.
For the happy path, this will be quite straightforward:
```
@Override
public Reservation updateReservation(String reservationId, Reservation reservation) {
Reservation reservationWithId= reservation.toBuilder().id(reservationId).build();
return saveReservation(reservationWithId);
}
```
For the happy path we simply need to attach the reservationId to the new `Reservation` object, and then call the `saveReservation()` method. Since we are providing an `id` field into the `reservationWithId`, a new reservation `id` will not be generated when we call the `saveReservation()` method and it will override the existing row with all the new info.
If you did get stuck at any point, please checkout branch [part-4-implementing-updateReservation](https://github.com/csapty12/restaurantManagementSystem/tree/part-4-implementing-updateReservation) and compare your code against mine.
### Implementing the deleteReservation method
Finally, we have to implement the logic around deleting a reservation.
As with the other CRUD methods, the `deleteById()` method is given to us out of the box. Under the hood, it will first find the row in the database with that `reservationId`, and if it exists, delete the row based on the reservationId provided.
```
@Override
public void deleteReservation(String reservationId) {
reservationRepository.deleteById(reservationId);
}
```
Since we are deleting the row in the database, when successful, the delete request will not need to return anything back to us as the data has been removed from the database.
If you did get stuck at any point, please checkout branch [part-4-implementing-deleteReservation](https://github.com/csapty12/restaurantManagementSystem/tree/part-4-implementing-deleteReservation) and compare your code against mine.
***WAHHHHOOOO!!*** We have *finally* come to the end of the `ReservationService` implementation. 🎉🎉🎉
That wasn't too stressful was it!? Fortunately, since we have only been focusing on the happy path, the logic needed was not *overly* stressful. When we dive into the unhappy path, you will also see how easy it is to handle for various types of errors, don't you worry!
For now though,***take a break, have a 🍫 and come back shortly***, as we only have one section left to go - implementing the Controller layer.
Check out [Part 5: Creating the Controller layer](https://hackmd.io/@csapty12/r1zDTxTCw) where we add in the last piece of the puzzle, and test our RMS 😎.