Should i write the checking (or other condition checking) inside the rest controller or write in service function? What should i write in rest api & What to write in service class function? And also, is this checking is valid or need to throw exception if data not found? Example:
Rest Controleer
#RequestMapping(value = "/employees", method = RequestMethod.GET)
public ResponseEntity<Object> getAllEmployee(){
List<Employee> employees = employeeService.getAllEmployee();
if (employees.isEmpty()) {
return RestResponse.generateResponse(HttpStatus.OK, true, "Data not found!", null);
}
return RestResponse.generateResponse(HttpStatus.FOUND, false, "Data found!", employees);
Service
public ResponseEntity<Object> getAllEmployee() {
List<Employee> employees = employeeRepository.findAll();
if (employees.isEmpty()) {
return RestResponse.generateResponse(HttpStatus.OK, true, "Data not found!", null);
}
return RestResponse.generateResponse(HttpStatus.FOUND, false, "Data found!", employees);
Any web application which follows the Spring MVC architecture, there will be usually three layers which will be defined.
Controller classes as the presentation layer. Keep this layer as thin as possible and limited to the mechanics of the MVC operations, e.g., receiving and validating the inputs, manipulating the model object, returning the appropriate MovedAndView object, and so on. All the business-related operations should be done in service classes. Controller classes are usually put in a controller package.
Service classes as the business logic layer. Calculations, data transformations, data processes, and cross-record validations (business rules) are usually done at this layer. They get called by the controller classes and might call repositories or other services. Service classes are usually put in a service package.
Repository classes as data access layer. This layer’s responsibility is limited to Create, Retrieve, Update, and Delete (CRUD) operations on a data source, which is usually a relational or non-relational database. Repository classes are usually put in a repository package.
So according to your requirement, you should choose the correct layer to write the logic.
Related
I'm designing a new project with microservices, the first principal which was already impleneted is that each microservice has its own DB schema. I have simple question about architecture.
I'll explain with simple example.
I've created a Location service. Here is some code from the controller:
#CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
#RestController
#RequestMapping("/locations")
#Slf4j
public class LocationController {
#Autowired
LocationService locationService;
#GetMapping("/Cntry")
public List<Country> getCountries() {
return locationService.getdCountries();
}
#GetMapping("/States/{id}")
public List<State> getStatesForCountry(#PathVariable("id") String countryId) {
List<State> states = locationService.getStatesForCountry(Integer.valueOf(countryId));
return states;
}
#GetMapping("/Cntry/{code}")
public Country getCountry(#PathVariable("code") String code) {
return locationService.getCountry(code);
}
As you can see above, the Location service which has local DB hold all the countries and states.
The location service hold more entities related to the project such as Location, so it is multi purpose microservice.
The problem I'm facing is that each microservice can have entities with country_id for example,
but when running it almost gurantee that it will need the country name which mean a service call (web client).
Here is sample of such service call:
#JsonIgnore
public String getCountryString() {
String url = MySpringBootServiceApplication.LOCATION_BASE_URL + "locations/CntryStr/" + countryId;
WebClient client = WebClient.create(url);
ResponseEntity<String> response = client.get()
.retrieve()
.toEntity(String.class)
.block();
String countryStr = response.getBody();
return countryStr;
}
I have two problems here that I need to solve:
Is there a solution (Architecture) to avoid calling to get the country string every time and from each micro service ?
A DTO in another service has country_id, but the user is looking for the name so, is there better way instead of make a webclient call inside DTO (doens't make sense).
Thanks for your help.
You can solve both problems by denormalizing: have each service maintain its own local mapping of country codes to names.
You can still have the location service govern updating country codes to names; it then publishes (in the rare case when a code-to-name mapping changes) events when they change for consumption by the other services to signal that they should update their respective mappings. Because the responsibility for updating this mapping is in one service while (many) other services have a responsibility for presenting the mapping, this is an example of the Command/Query Responsibility Segregation pattern (CQRS).
In general taking a normalized relational schema and turning it into a microservice architecture leads to the issue you're facing. Denormalization can avoid the problem, but then focusing on the operations (the verbs) rather than the data (the nouns) lead to a much more effective and easier to work with architecture.
I do have some entity class (code without annotations for simplified example)
class User {
public String id;
public String name;
}
Now I want to output this via an API, but I want to structure my response in a special format, like
{
"data": {
"id": 1,
"name": "mars3142"
}, // user object or another entity or list...
"meta": ...,
"error": ...
}
The meta and/or error data should only be visible in special situations (like RuntimeExceptions). Where is the best place to transform my entity results into the normalized response? Do I need to write a filter for that? Does anybody has a sample code for that?
I would suggest to implement something this:
public abstract class BaseResponse {
// Meta data
// Consider defining fields here needed for happy-path and error-responses
// Contains common tracking fields, e.g. correlationId, requestId
}
public class ErrorResponse extends BaseResponse {
// Error Fields
}
public class Response extends ErrorResponse {
// Entity-object in your case
}
I guess you can build your response like setting response from DAO to above suggested structure in controller layer. For error-responses (in case of RuntimeExceptions), they're standardly build and returned in #ControllerAdvice or other.
Some patterns of exception handling are explained in Error Handling for REST with Spring | Baeldung.
Regarding your 2 questions:
Design: The proper place for this response-mapping depends on the scope (all responses or just some) and existing components in your application's response layer.
Patterns and Web-Framework concepts: I would not use the response-filters or -interceptors of your web-framework. Those should be used for cross-cutting concerns, or for chained processes (e.g. security, authorization, enrichment, sanitation).
Instead I would use the web-frameworks concepts and components that are responsible for response-representations, like ResponseEntity (HTTP-response representation, ControllerAdvice (error-handling), HttpMessageConverter.
There are 3 ways you could "wrap" your objects into uniform JSON-response models:
Annotate class with the custom #JsonRootName as data and in special cases add meta and/or error attributes (through e.g. embedding into a wrapper or using a mixin)
A JSON custom serializer that could extend from BeanSerializer which wraps this and any class uniformly in your given outer structure
Modify Spring's MappingJackson2HttpMessageConverter to wrap any returned response object into the predefined JSON-structure
You could iterate from the simplest (1.) to the most complex (3.). Some iteration code (like 2.) can be reused in the next (3.).
1. Use a Wrapper Class
The first is rather a simple start where you can implement the "normalization" within controller-methods. You could for example put the object (serialized as data) into the "empty" meta-structure (wrapper-class) with an empty JsonNode, and meta or error properties.
2. Define a Custom Serializer
The second is pretty flexible and can be tested well in isolation (not even depending on Spring). It would allow to implement the complete object-wrapping in one place.
3. Customize Spring's HTTP Message Converter
The third is similar to the second but requires some knowledge about Spring's message-converters and allows you to transform each response-object to a specific JSON-response using Jackson's ObjectMapper.
Sample code can be found online, e.g. at Baeldung's Jackson or Spring tutorials, Springframework Guru articles.
I used the solution from https://stackoverflow.com/a/72355056/708157 and transformed it a little bit.
Now my classes are that way
public class BaseResponse<T> {
boolean success;
T data;
Error error;
}
public class Error {
...
}
And every api response is now ResponseEntity<BaseResponse<XYZ>>. This way, I can setup my default structure and my classes are lose coupled, because I can use every class for T within my BaseResponse.
In our Angular + Spring boot application application, we have 2 Controllers (2 Services are internally referenced). In first controller, We are sending a File from UI and reading the content of the file , query an external application and retrieve a set of data and return only a sub-set of Data, for entering as recommendation for UI fields. why we are returning only sub-set of data received from the external application? Because, we need only those sub-set data for showing recommendations in UI.
Once the rest of the fields are filled, then, we call another controller to generate a report. But, for generation of files, the second service requires the rest of the data from external application, which is received by the first service. I understand that Autowiring the first service in the second service, will create new instance of the first service and I will not get the first service instance, which is used to query the external application. I also like to avoid calling the external application again to retrieve the same data again in the second service. My question is how to fetch the data received by the first service in the second service?
For example:
First controller (ExternalApplicationController), which delegates loading of loading/importing of data from files
public class Department{
private Metadata metadata; // contains data such as name, id, location, etc.,
private Collection<Employee> employees; // the list of employees working in the department.
}
#RestController
#RequestMapping("/externalApp")
public class ExternalApplicationController{
#Autowired
private ExternalApplicationImportService importService;
#PostMapping("/importDepartmentDataFromFiles")
public Metadata importDepartmentDataFromFiles(#RequestParam("files") final MultipartFile[] files) {
return this.importService.loadDepartmentDetails(FileUtils.getInstance().convertToFiles(files)).getMetadata();
}
}
The first service (ExternalApplicationImportService), which delegates the request to the external application for loading of department data.
#Service
public class ExternalApplicationImportService{
private final ExternalApp app;
public Department loadDepartmentDetails(File file){
return app.loadDepartmentDetails(file);
}
}
The Metadata from the ExternalApplicationController is used to populated UI fields and after doing some operations (filling up some data), user requests to generate a report(which contains details from the employees of that department)
#RestController
#RequestMapping("/reportGenerator")
public class ReportController{
#Autowired
private ReportGenerationService generationService;
#PostMapping("/generateAnnualReports")
public void generateAnnualReports(){
generationService.generateAnnualReports();
}
}
#Service
public class ReportGenerationService{
public void generateAnnualReports(){
//here I need access to the data loaded in the ExternalApplicationImportService.
}
}
So, I would like to access the data loaded in the ExternalApplicationImportService in the ReportGenerationService.
I also see that there would be more services created in the future and might need to access the data loaded in the ExternalApplicationImportService.
How can this be designed and achieved?
I feel that I'm missing something how to have a linking between these services, for a given user session.
Thanks,
Paul
You speak about user session. Maybe you could inject the session of your user directly in your controllers and "play" with it?
Just adding HttpSession as parameter of your controllers' methods and spring will inject it for you. Then you just have to put your data in the session during the first WS call. And recover it from the session at the second WS call.
#RestController
#RequestMapping("/reportGenerator")
public class ReportController{
#PostMapping("/generateAnnualReports")
public void generateAnnualReports(HttpSession session){
generationService.generateAnnualReports();
}
}
Alternatively for the second call you could use:
#RestController
#RequestMapping("/reportGenerator")
public class ReportController{
#PostMapping("/generateAnnualReports")
public void generateAnnualReports(#SessionAttribute("<name of your session attribute>") Object yourdata){
generationService.generateAnnualReports();
}
}
You are starting from a wrong assumption:
I understand that Autowiring the first service in the second service, will create new instance of the first service and I will not get the first service instance, which is used to query the external application.
That is not correct: by default, Spring will create your bean as singleton, a single bean definition to a single object instance for each Spring IoC container.
As a consequence, every bean in which you inject ExternalApplicationImportService will receive the same instance.
To solve your problem, you only need a place in where temporarily store the results of your external app calls.
You have several options for that:
As you are receiving the same bean, you can preserve same state in instance fields of ExternalApplicationImportService.
#Service
public class ExternalApplicationImportService{
private final ExternalApp app;
// Maintain state in instance fields
private Department deparment;
public Department loadDepartmentDetails(File file){
if (department == null) {
department = app.loadDepartmentDetails(file);
}
return department;
}
}
Better, you can use some cache mechanism, the Spring builtin is excellent, and return the cached result. You can choose the information that will be used as the key of the cached data, probably some attribute related to your user in this case.
#Service
public class ExternalApplicationImportService{
private final ExternalApp app;
#Cacheable("department")
public Department loadDepartmentDetails(File file){
// will only be invoked if the file argument changes
return app.loadDepartmentDetails(file);
}
}
You can store the information returned from the external app in an intermediate information system like Redis, if available, or even in the application underlying database.
As suggested by Mohicane, in the Web tier, you can use the http sessions to store the attributes you need to, directly as a result of the operations performed by your controllers, or even try using Spring session scoped beans. For example:
#RestController
#RequestMapping("/externalApp")
public class ExternalApplicationController{
#Autowired
private ExternalApplicationImportService importService;
#PostMapping("/importDepartmentDataFromFiles")
public Metadata importDepartmentDataFromFiles(#RequestParam("files") final MultipartFile[] files, HttpSession session) {
Deparment department = this.importService.loadDepartmentDetails(FileUtils.getInstance().convertToFiles(files));
session.setAttribute("department", department);
return deparment.getMetadata();
}
}
And:
#RestController
#RequestMapping("/reportGenerator")
public class ReportController{
#Autowired
private ReportGenerationService generationService;
#PostMapping("/generateAnnualReports")
public void generateAnnualReports(HttpSession session){
Department department = (Department)session.setAttribute("department");
// Probably you need pass that information to you service
// TODO Handle the case in which the information is not present in the session
generationService.generateAnnualReports(department);
}
}
In my opinion, the second of the proposed approaches is the best one but all are valid mechanisms to share your data between the two operations.
my recommendation for you will be to revisit your design of classes and build a proper relationship between them. I feel you need to introduce the extra logic to manage your temporal data for report generation.
#Mohicane suggested to use HTTP Session in above answer. It might be a possible solution, but it has an issue if your service needs to be distributed in the future (e.g. more than one runnable instance will serve your WEB app).
I strongly advise:
creating a separate service to manage Metadata loading process, where you will have load(key) method
you need to determine by yourself what is going to be a key
both of your other services will utilize it
this service with method load(key) can be marked by #Cacheable annotation
configure your cache implementation. As a simple one you can use In-Memory, if a question becomes to scale your back-end app, you can easily switch it to Redis/DynamoDB or other data storages.
Referances:
Spring Caching
Spring Caching Guide
I want to seek a best practice for applying business rules when working with spring data rest.
Lets consider following scenario:
I have a Customer and Order in #OneToMany relationship.
I have a business rule saying that Customer needs to have verified flag set to be able to make orders
So I need to make sure that whenever someone POSTs to /orders the Customer making the call is verified.
I'm considering using beforeSave Validators autowiring other service/repositories into the Validator and check whatever needs to be checked.
Is there better way of achieving the same?
There are several ways to solve this. As far as my knowledge goes:
Usage of spring security annotations like #PreAuthorize. The intended use of these annotations is however for security purposes and you are mentioning business rules. I would use these for user authorization rules Spring data rest security chapter
The use of validators as you mentioned yourself. Spring data rest Validators
Use spring data rest events Spring data rest events. You can create global event handlers, however here you need to determine the entity type. I would go with Annotated event handlers to perform business logic Spring data rest annotated event handler
So just for the sake of world piece I'm adding my solution. Went with #2.
The documentation is pretty clear on how to proceed so just sharing few tips which may save you time.
You need to assign validators manually, auto-discovery doesn't work
Manually spelling event type is error prone, some helper Enum could be handy.
Like:
/**
* "beforeSave" gets called on PATCH/PUT methods
* "beforeCreate" on POST
* "beforeDelete" on DELETE
*/
enum Event {
ON_CREATE("beforeCreate"), ON_UPDATE("beforeSave"),
ON_DELETE("beforeDelete");
private String name;
Event(String name) {
this.name = name;
}
}
...
private static void addValidatorForEvents(ValidatingRepositoryEventListener eventListener, Validator validator, Event... events) {
Arrays.asList(events).forEach(event -> eventListener.addValidator(event.name, validator));
}
One out of the box solution you can use to solve your Business rules related problems, is using Spring AOP. What you can do, is define an Annotation (say #X) and place that annotation on top of your POST call.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface X{}
Next what you need to do is, create an aspect, and run your custom validation logic in this aspect as follows,
#Aspect
#Component
public class CustomAspect {
//You can autowire beans here
#Around("#annotation(qualified name of X)")
public Object customMethod(ProceedingJoinPoint joinPoint) throws Throwable {
flag = customLogic();
if (flag){
return joinPoint.proceed(); //return if logic passes, otherwise
}else{
throw new BusinessRuleException("Business rule violated");
}
}
private boolean customLogic(){
//your custom logic goes here
}
}
And finally apply this annotation on top of any method in controller layer like:
#X
#RequestMapping(method = RequestMethod.POST, value = "do-something")
public void callSomething(HttpServletRequest request) throws Exception {
// your business logic goes here
}
Only thing to note above is that you need to pass HttpServletRequest request explicitly to your controller method in order to AOP aspect get the same context for manipulation of user session related attributes like session_id, etc.
Above solution will help you add business rules on top of your Business Logic and help you with all kinds of pre validations you want to build in your web application. It is a pretty handy application of Spring AOP. Do reach out in case of any
For an MVC3 application I want to create a reusable DAL that is accessed as a service, as this will be used for other projects in the future.
I created all the entities with TT templates and EFCodeFirst in a separate project, then consumed it in a RESTful WCF service.
The structure of this service seems a bit different from other WCF services I have written where I have specified RESTful signatures and optional JSON responses as method decorators in the service's interface, ie:
[WebGet(UriTemplate = "GetCollection")]
public List<SampleItem> GetCollection()
{
// TODO: Replace the current implementation to return a collection of SampleItem instances
return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } };
}
[WebInvoke(UriTemplate = "", Method = "POST")]
public SampleItem Create(SampleItem instance)
{
// TODO: Add the new instance of SampleItem to the collection
throw new NotImplementedException();
}
Where this RESTful WCF service (created from the RESTful WCF option) differs is that there is no interface, and I can decorate the service methods with what I need - that's fine. The service will expose methods like GetUserByID(int id), etc.
The issue is that I want to use this in a MVC3 application, and am not clear on how to hook up the models to my service and would like some direction in accomplishing this.
Thanks.
Assume you want to expose an entity called Person. The WCF REST service may look as follows:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public partial class PeopleWebService
{
[WebGet(UriTemplate = "")]
public List<Person> GetCollection()
{
try
{
IPeopleRepository repository = ServiceLocator.GetInstance<IPeopleRepository>();
var people = repository.GetPeople();
// use automapper to map entities to Person resource
var result = Mapper.Map<List<Person>>(people);
return result;
}
catch (Exception exception)
{
// do logging etc
throw new WebFaultException(HttpStatusCode.InternalError);
}
}
/* other methods */
}
These services can be generated by T4 too.
Notice that there is no need for an interface on the WCF service itself. I generally do not expose any database entities directly in WCF services as my services evolve differently than my database entities. Once an API is published it should pretty much remain the same. This prevents me from changing my database schema to fit new requirements.
Instead I map my entities to resources. So Person may looks as follows:
[DataContract]
public class Person
{
[DataMember]
public string GivenName { get; set; }
/ * more properties */
}
It may be a good thing to use T4 to generate these as well. Routing is defined something like this:
public void Register(RouteCollection routes)
{
routes.AddService<WorkspaceWebService>("api/v1/people");
}
To consume it from the ASP.NET MVC project, you can share your resources (aka Person) as defined above as an assembly, or you can use T4 to generate a separate set of resources that are almost the same, but with some additional attributes needed for ASP.NET MVC, like those used for validation. I would generate it, because my ASP.NET MVC view models generally evolve independently to my REST resources.
Lets assume your REST service runs at https://api.example.com/ and your MVC website is running at https://www.example.com/. Your PeopleController may look as follows.
public class PeopleController : ControllerBase
{
[HttpGet]
public ActionResult Index()
{
return View(Get<List<Person>>(new Uri("https://api.example.com/api/v1/people")));
}
protected T Get<T>(Uri uri)
{
var request = (HttpWebRequest) WebRequest.Create(uri);
request.Method = "GET";
request.ContentType = "text/xml";
using (var response = (HttpWebResponse) request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
Debug.Assert(responseStream != null, "responseStream != null");
var serializer = new DataContractSerializer(typeof (T));
return (T) serializer.ReadObject(responseStream);
}
}
}
}
From your question, I assume you want to use JSON. For that you just have to set the appropiate ContentType on the request and use the DataContractJsonSerializer rather than DataContractSeralizer. Note that there are some issues with dates and DataContractJsonSerializer. The WCF rest service will automatically return XML if the contenttype is "text/xml" and JSON if it is "application/json".
Notice that the MVC application has no knowledge of the database, the database entities or its database context. In fact there is no database logic in the MVC application. You will have to pay close attention to security, because the user context is missing from the WCF rest services. But, that is a whole different discussion.
The way to think about this is that your MVC app now only knows about your service. It has no clue that there's a DAL behind it. Basically, consider the service your "Persistence" layer. So, now your MVC model must populate itself using the service. So, just like any other application would populate a service, that is how your model will populate itself. Then your controllers will use your model return your views.
That wasn't the nitty gritty you may be looking for, but there are plenty of resources out there on how to consume RESTful services in .NET. Check those out and get those populating your model. Then get your model to your view.