For spring project, Do i need to inject pagerequest object? - spring-boot

I read some articles conecerning clean architecture.
These articles say that business layer should not know how to solve domain knowledge directly. (only what to do)
PageRequest object is provided by spring framework.
And this object implements Pageable interface.
My question is...
Do i create the Pagerequest in business layer (service)
or inject it to the layer?

You said the PageRequest / Pageable object is provided by Spring, so - if you are using Spring MVC - I would expect something like the following on controller level:
#RequestMapping(method = ..., path = ..., produces = ...)
public ... readSomethingPaged(#PathVariable String ..., Pageable pageable) {
...
}
Then of course you have to pass this Pageable (created automagically by Spring using the request parameters page=...&size=...&sort=...) through your service layer down to the place you are getting the data from. Within Spring (Boot) a Spring Data JPA repository comes in handy. It accepts the Pageable created by the controller and returns the appropriate data.
Or what exactly do you mean with "Do i create the Pagerequest in business layer"?
If you really have to create your own PageRequest it has to be done before the repository is called, then imho not the service itself but the controller / a facade should be responsible for that.

Related

spring boot rest webservice, how to improve a clean code?

i started a project on spring boot using a rest a webservice, when i shared it between my team they puted some comments :
get method need to be grouped Ex : get/users & get/users/{id} will be get/users/{id}
remove put method & just use post Ex: post/users/0 add | post/users/{id} update
make a helper class for Jdbc Template and call it in the repository classes to centralize the code
pls guys help me to solve this i'm so confused, and thank you
get method need to be grouped Ex : get/users & get/users/{id} will be
get/users/{id}
I do not agree with this. /get/users will be returning List<User> and get/users/{id} will return User that matches with {id}
remove put method & just use post Ex: post/users/0 add |
post/users/{id} update
Post should be used when you create a new resource. POST is not idempotent. Each time you call a post a new resource will be created.
e.g. Calling POST /Users will create a new User every-time.
PUT on other hands works like upsert. Create if the resource is not present and update/replace if present. Put is idempotent and doesn't change the resource's state even if it's called multiple times.
make a helper class for Jdbc Template and call it in the repository
classes to centralize the code
Helper classes help to separate the concerns and achieve single responsibility.
However, JdbcTemplate is a ready to use abstraction of JDBC. I don't see any point in creating Helper. You can create a DataAccessObject (DAO) or Repository which has-a JdbcTemplate. Like the two Dao shown below
public class UserDao {
#Autowired
private JdbcTemplate jdbcTemplate;
public User findUserById(String id){}
public void addUser(User user){}
}
// -------
public class BooksDao{
#Autowired
private JdbcTemplate jdbcTemplate;
public List<Book> getAllBooksByType(String type){}
public void Book getBookByName(String name){}
}
Now, your Dao objects can be called from Controller or if you need to modify data before/after DB operation, best is to have a Service layer between Controller and Dao.
Don't bother too much about recommendations or rules. Stick to the basic OOPS concepts. Those are really easy to understand and implement.
Always:
Encapsulate data variables and methods working on those variables together
Make sure your class has a Single Responsibility
Write smaller and testable methods (if you can't write tests to cover your method, then something is wrong with your method)
Always keep the concerns separate
Make sure your objects are loosely coupled. (You are already using spring so just use the spring's auto-wiring)

How to use Spring Pageable object in controller without using Java Data?

I would like to ues Pageable object in controller's method to eliminate the redundant page and size parameters. Just like the following:
#RequestMapping("/list")
public String list(Model model , Pageable pageable) {
logger.info("pageable = {}" , pageable);
// ... skipped
}
Instead of:
#RequestMapping("/list")
public String list(Model model , Integer page, Integer size) {
logger.info("page = {}, size = {}" , page, size);
// ... skipped
}
However, after adding the org.springframework.boot:spring-boot-starter-data-jpa dependency to my pom.xml, I've been always asked for setting up the 'entityManagerFactory' bean, which looks like something from the embedded hibernate dependency.
How can I use the Pageable obejct and get rid of the Spring Data things?
Pageable is part of the Spring Data Commons project -
and I've never seen it used outside of Spring Data.
That said... it may be possible, but you'd have to pull in the appropriate jar containing the Pageable class, without pulling in any of the Spring Boot Starter Data - * dependencies. If you add the starter, Spring boot is going to attempt to perform automatic datasource configuration, which it sounds like you do not want.
You could try to pull in the single dependency that contains that class (org.springframework.data:spring-data-commons i believe). You should then have access to the Pageable class without any autoconfiguration.
Try to use spring data JDBC. This would help you avoid the need to use JPA!
Here is a good source of information about jdbc: Github-Spring Data JDBC generic DAO implementation

Relax Security for a Spring Data REST Projection

I have a User class and I want to authorize access such that only a user gets to see what he is entitled to.
This was easily achievable using Spring Security in conjunction with Spring Data Rest where in JPA Repository I did below -
public interface UserRepository extends JPARepository<User,Integer> {
#PreAuthorize("hasRole('LOGGED_IN') and principal.user.id == #id")
User findOne(#Param("id") Integer id);
}
In this way, a user when visits to Spring Data REST scaffolded URLs like -
/users/{id}
/users/{id}/userPosts
Only those logged in with {id} get to see these and everyone else gets 401 like I would have wanted.
My problem is that I have one of Projections which is a public view of each user and I am crating it using Spring Data Rest projections as below which I want to be accessible for every {id}
#Projection(name = "details", types = User.class)
public interface UserDetailsProjection {
..
}
So, /users/{id1}?projection=details as well as /users/{id2}?projection=details should give 200 OK and show data even though user is logged in by {id1}
I began implementing this by marking projection with #PreAuthorize("permitAll") but that won't work since Repository has harder security check. Can we have this functionality where for a projection we can relax security ?
I am using latest Spring Data Rest and Spring Security distributions
Seems reasonable to add a custom controller for this use-case.
Please also consider:
Evaluate access in projections using #Value annotations
Add another entity for the same database data but with different field set for read-only operations, e.g. using inheritance (be careful with caching, etc.) - depends on your data storage type
Modify model to split User entity into two different entities (profile, account) since they seem to have different access and possibly even operations
You can also add a ResourceProcessor<UserSummaryProjection> to evaluate access programmatically and replace resource content (projection) with a DTO
Example of evaluating access in projections with #Value annotations:
#Projection(types = User.class, name = "summary")
public interface UserSummaryProjection {
#Value("#{#userSecurity.canReadEmail(target) ? target.email: null}")
String getEmail();
}
Added spring security code in the data access layer is not a good idea. I would suggest you to add the #PreAuthorize annotation to the controller/service method. Since you have a query parameter, ?projection=details, you can have separate controller/service method for the details projection.
Add following to your details projection method:
#RequestMapping("/url", params = {"projection"})
#PreAuthorize("hasRole('LOGGED_IN') and principal.user.id == #id")

Spring DTO validation in Service or Controller?

I'm building a straight forward AJAX / JSON web service with Spring. The common data flow is:
some DTO from browser
v
Spring #Controller method
v
Spring #Service method
I'm looking for the most easy way to handle data validation.
I know the #Valid annotation which works pretty well inside #Controller methods.
Why does #Valid not work within #Service methods?
I mean: A service method can be used by any other service and controller. So wouldn't it make much more sense to validate at #Service level?
Let's take this simple example:
MyDTO.java:
public class MyDTO {
#NotNull
public String required
#Min(18)
public int age;
}
MyServiceImpl.java:
public MyDomainObject foo(MyDTO myDTO) {
// persist myDTO
// and return created domain object
}
MyController.java:
#Autowired
MyService myService;
#Autowired // some simple bean mapper like Dozer or Orika
Mapper mapper; // for converting domain objects to DTO
#RequestMapping(...)
public MyDomainObjectDTO doSomething(#RequestBody MyDTO myDTO) {
mapper.map(myService.foo(myDTO), MyDomainObjectDTO.class);
}
Is it common practice that the service method receives the DTO?
If yes: What's the best practice to validate that DTO inside the service method?
If no: Should maybe the controller manipulate the Domain object and just let the service save that object? (this seems pretty useless to me)
In my opinion the service should be responsible for only data consistency.
How do you solve this?
My answer? Both.
The service must check its own contract for validity.
The controller is part of the UI. It should validate and bind for a better user experience, but the service should not rely on it.
The service cannot know how it's being called. What if you wrap it as a REST service?
The service also knows about business logic violations in a way that no UI can. It needs to validate to make sure that the use case is fulfilled appropriately.
Double bag it; do both.
See my other answer: Check preconditions in Controller or Service layer
If you really want to do validation like error handling in your Service layer similar to Spring MVC you can use javax.validation and AspectJ (to advice the methods to validate) which is what I do because I like making reflection do the work and declarative programming (annotations).
Spring MVC doesn't need to do AspectJ/AOP to do the error handling because the methods are being called through reflection (url routing/dispatching).
Finally for you MVC code you should know that #Valid is sort of unofficially deprecated. Instead consider #Validated which will leverage more of the javax.validation features.

Spring boot actuator - Implement custom metrics

I would like to implement custom metric or statistics to my spring boot rest web service using actuator but i am not able to find simple tutorials.
For example:
how to show how many times a certain controller was called and what exact parameter field was filled?
how can i create a metric that when its URL is called, it runs certain query and shows back a json with some result
This seems like a good scenario for AOP (Aspect Oriented Programing) as this will allow you to separate this statistic logic from the business logic.
Have a look at Spring doc for more info about AOP and how to achieve that with Spring.
You can then define a pointcut on your controller and have a service for counting (and probably then storing) the data.
Refer below link
AOP Example
For point two the solution is to create an endpoint class (it can be or not a rest controller class). For example:
#Component
#RestControllerEndpoint(id = "pfm-statistics")
public class StatisticsEndpoint {
#GetMapping(value = "/", produces = "application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet")
#ResponseBody
public byte[] generateStatisticsAsExcel() {
...
Note that the ID is the path to be called from URL. We can create a simple endpoint too and just return a string if we want. In this case instead of #RestControllerEndpoint annotation we can use #Endpoint, as a side note, the id should always contain dash

Resources