When using Spring, call local method directly or use API - java

Just started learning how to use Spring! As a best practice if you are working on a spring method and you need to add/leverage a piece of functionality that you've already built out in another Spring Class which is accessible via the API, should you use the established API or call it directly?

Suppose you have a CustomerService component
#Service
public class CustomerService {
public Customer getCustomerById(CustomerId id) {
//your code here
}
}
Suppose now that you have an OrderService that needs to find the customer before placing an new order. You definitively would prefer to find the customer using your existing CustomerService API.
#Service
public class OrderService {
#Autowire private CustomerService customerService;
public void placeOrder(Order order, CustomerId custId) {
Customer customer = customerServive.getCustomerById(custId);
//your code here
}
}
That totally make sense.
Now suppose you have a controller to expose your CustomerService to your web clients.
#RestController
public CustomerController {
#Autowire private CustomerService customerService;
#GET("/customer/{custId}")
public Customer getCustomer(#Param CustomerId custId){
return customerService.getCustomerById(custId);
}
}
From you OrderServer you definitely don't need to/should not make an HTTP remote call to this HTTP service to get a customer. That would not make sense if they are both colocated in the JVM. It is a hell of a lot simpler and safer to just use your local service.
If however your CustomerService runs in a different process/JVM, one entirely different than the one it runs your OrderService than it would make sense to make a remote HTTP call to get your customer.
In a case like this, you probably would have a CustomerServiceGateway to make the remote call.
For example, in the orders api
interface CustomerService {
Order getCustomerById(CustomerId custId);
}
And then a gateway implementation:
#Service
public class CustomerServiceGateway implements CustomerService {
#Autowire private RestTemplate restTemplate;
Order getCustomerById(CustomerId custId) {
return restTemplate.getForObject("http://customer-api/customer/{custId}", custId);
}
}
This is an oversimplification, but even so you can see that is much harder to do and it only make sense if you try to invoke remote services.

Related

Java Spring boot: Repository & Service

please can you explain me shortly for what are responsible Repository class and Service class in Spring boot. As I know repo is for doing all the database operations and interacting with DB but somewhere I heard from videos that Service class talks to DB so I got confused and can't find any definition for them.
Thank you very much for your time.
#Service assigns Beans to handle logic
#Repository assigns Beans to take care of communicating with the DB
Service class is where you perform your business logic which you do not want the user to view and repository class is where you perform database operations on an entity.
There is one more class called controller which is used to interact with web requests which are then forwarded to service methods and if there is need for data from database they send it forward to repository class.
Hope this explains. It is usually a design pattern for building production level applications
Here is a short example
#Controller // Controller class
public class RequestController{
#Autowired
private ServiceClass service;
#RequestMapping("")
public string index(#Param("name") String name){
return service.getString();
}
#Service
public class ServiceClass{
#Autowired
private StuRepository repo;
public String getString(String name){
if(name.equals("Rahul")
return repo.findName();
else
throw new Error("business logic performed here");
}
#Repository
public interface StuRepository extends JpaRepository<Model,Integer>{
String findName();
}

Access to Spring Boot / MongoDB controller-classes from other classes

I'm making a REST API with Spring Boot and MongoDB in Java.
I've got, for instance, a Location class, with a corresponding LocationRepository and LocationController classes.
To access the Location-database (LocationRepository), I need to do it through the LocationController (as far as I know). I've got functions in this controller that run on POST/GET requests, and it's working just fine when sending requests from the front-end server (Nuxt.js w/ Axios) or just through Insomnia/Postman.
However, if I wan't to access the Location-database from the backend, and from another class in my program, I'm not sure how to do it.
I suppose I either need to send API-requests locally, or I need to get the actual instance of my LocationController to run functions on it.
Is sending API-requests locally like this bad practice?
Can I somehow get a hold of the instance of my LocationController? This is all managed through Spring, which I assume instanciates the controllers somewhere, but I have no idea where, or how to get a hold of them.
Here is an example of one of my controllers:
#RestController
#RequestMapping("api/locations")
public class LocationController {
#Autowired
private LocationRepository repository;
#RequestMapping(value = "/all", method = RequestMethod.GET)
public List<Location> getAllLocations(#RequestBody String body) {
return repository.findAll();
}
}
Appreciate any help!
EDIT: Using #Autowired LocationController locationController in any class that needs access to this seems to work.
You don't have to instantiate your controller, your repository is in charge of fetching data from database. You can do exctly the same that you're doing inside your controller :
public class MyGreatClass{
#Autowired
private LocationRepository repository;
public void myGreatMethod() {
List<Location> locations = repository.findAll();
// Do the stuff with location
}

Testing a custom RepositoryRestController that uses a PersistentEntityResourceAssembler

I have a RepositoryRestController that exposes resources for some persistent entities.
I have a method on my controller that takes a PersistentEntityResourceAssembler to help me generate the resources automatically.
#RepositoryRestController
#ExposesResourceFor(Customer.class)
#RequestMapping("/api/customers")
public class CustomerController {
#Autowired
private CustomerService service;
#RequestMapping(method = GET, value="current")
public ResponseEntity getCurrent(Principal principal Long id, PersistentEntityResourceAssembler assembler) {
return ResponseEntity.ok(assembler.toResource(service.getForPrincipal(principal)));
}
}
(Contrived example, but it saves going into too much detail about irrelevant details of my use-case)
I'd like to write a test for my controller (my real use-case is actually worth testing), and am planning on making use of #WebMvcTest.
So I have the following test class:
#RunWith(SpringRunner.class)
#WebMvcTest(CustomerController.class)
#AutoConfigureMockMvc(secure=false)
public class CustomerControllerTest {
#Autowired
private MockMvc client;
#MockBean
private CustomerService service;
#Test
public void testSomething() {
// test stuff in here
}
#Configuration
#Import(CustomerController.class)
static class Config {
}
}
But I get an exception saying java.lang.NoSuchMethodException: org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler.<init>()
Presumably something is not being configured correctly here because I'm missing the entire data layer. Is there some way of mocking out the PersistentEntityResourceAssembler? Or another approach I could use here?
I ended up for now with:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
The downsite of it is that the test would start the full Spring application context (but without the server).
I ended up doing a slightly hacky solution here:
I removed PersistentEntityResourceAssembler from the controller method.
I added an #Autowired RepositoryEntityLinks to the controller, on which I call linkToSingleResource to create the links as needed.
I added an #MockBean RepositoryEntityLinks to my test class, and configured the mocking to return something sensible:
given(repositoryEntityLinks.linkToSingleResource(any(Identifiable.class)))
.willAnswer(invocation -> {
final Identifiable identifiable = (Identifiable) invocation.getArguments()[0];
return new Link("/data/entity/" + identifiable.getId().toString());
});
It's far from ideal - I'd love to know if there's a way of getting just enough of the data layer up that I can depend on PersistentEntityResourceAssembler.

Using REST calls in Spring to expose service layer

I have an existing Service layer of Java code that I'd like to use in some REST calls. The way I'd like to do this is to have a user pass in a service ID in the URL, and then on the backend lookup the service and method (in a DB or config file) and call it. For example:
http://foobar.com/rest/car
When this URL is called, I would take the serviceId of "car" and call the CarService. I imagine I'd have a simple configuration:
car=com.foobar.services.CarService
house=com.foobar.services.HouseService
etc..
Is there a way to do this using Spring? One concern I have is not calling the service, but figuring out which method to call. If I had a call to http://foobar.com/services/car/red - how would I pass in the method parameter of 'red' and decide which method to call?
Here's an example of what this would look like in Java:
#RequestMapping(value = "{serviceId}")
#ResponseBody
public Object getMarshalledObject(#PathVariable String serviceId) {
if ("car".equals(serviceId)) {
return getCar();
}
throw new ServiceNotFoundException("Service ID not found.");
}
I would make separate controllers for each service, and have each controller delegate to its corresponding service after it extracted the relevant information from the request.
Due to the nature of #RequestMapping on controllers and their methods, this should be pretty easy:
#RequestMapping("/car")
class CarController {
#Autowired
private CarService service;
#RequestMapping("/{color}")
public Object getCarsByColor(#PathVariable String carColor) {
return service.getCarsByColor(houseColor);
}
}
#RequestMapping("/house")
class HouseController {
#Autowired
private HouseService service;
#RequestMapping("/{houseId}")
public Object getHouseById(#PathVariable int houseId) {
return service.getHouseById(houseId);
}
}
What we have here is two different controllers, with different services, that are mapped by the #RequestMapping that is applied to the class. Further, the controller methods are called by the remaining path elements from the url.
Instead of a simple properties file where you have this...
car=com.foobar.services.CarService
house=com.foobar.services.HouseService
...configure Spring (in the appropriate dispatch configuration file) to manage those beans for you:
<bean id="car" class="com.foobar.services.CarService" />
<bean id="house" class="com.foobar.services.HouseService" />
Assuming your service classes implement a common interface (for example, com.foobar.services.BaseService), in your controller you can autowire them up like so:
#Autowired
#Qualifier("car")
private BaseService _carService;
#Autowired
#Qualifier("house")
private BaseService _houseService;

How to get my Dao methods exposed in my Service layer?

My Dao's are setup like:
UserDao
UserDaoImpl
My business layer are in service classes like:
UserService
UserServiceImpl
Now my Dao's have basic operations like get, update, save, etc.
I want to expose these methods in my service class without having to manually add these to each service interface.
How can I achieve this?
The point of having services is to aggregate DAO methods within a transaction, and to add business logic. If all your services are doing is wrapping individual DAO methods there is no point to having them. Using some framework like Grails (or Play, so you can still use Java if you want) data access methods are added to your domain objects dynamically, you can call them from the controller and have a lot less CRUD code to mess with (but still have services as an option in the event you need them later).
If the service is doing nothing else besides CRUD operations, and no other service has need for that DAO, I'd see no reason why you can't just put those operations in the service implementation and dispense with the DAO.
It's easy enough to just use the service as a pass-through:
public interface FooService {
Foo find(Long id); // Same as DAO signature
}
#Service
public class FooServiceImpl implements FooService {
#Resource(name = "fooRepository")
private FooRepository fooRepository;
public Foo find(Long id) { return this.fooRepository.find(id); }
}
It's possible to write a generic DAO:
public interface GenericRepository<V, K extends Serializable> {
V find(K key);
List<V> find();
K save(V value);
void update(V value);
void delete(V value);
}
Yes this is kind of variation of Adapter pattern
You can also extend the UserDaoImpl to UserServiceImpl for example
UserServiceImpl extend UserDaoImpl implements UserService{
// Rest of the Service implementation
}

Categories