Currently I have a domainObjectPersistenceService which calls DAO functions on the Domain Object, and I have a separate domainObjectDomainService which processes Business Logic. For example, userPersistenceService and userDomainService.
I am unsure whether to handle the initial call to the domainObjectPersistenceService from the Controller directly, or call it from inside the domainObjectDomainService.
What is the preferred way?
#Controller
public class Controller {
public controllerMethod(int fileId) {
domainObject = domainObjectPersistenceService.getFile(fileId);
data = domainObjectDomainService.processFile(domainObject);
// convert data into DTO
return dataDTO;
}
}
or
#Controller
public class Controller {
public controllerMethod(int fileId) {
// domainObjectDomainService handles persistence layer calls.
data = domainObjectDomainService.processFile(fileId);
// convert data into DTO
return dataDTO;
}
}
Many thanks!
See Domain Driven Design: Domain Service, Application Service
In your case Controller is Application Service. It is an orchestrator, that retrieves objects from the repository and pass to the Domain Service.
First way is correct.
Related
We have two products: Product A and Product B and both have different rest controllers. Both of these controllers are calling the same common service and the common service calling the common dao methods. I want to select the schema in the common daos methods based on which products the call has been made. So for example, if the call is coming from the Product A controller then select schema as A or else B.
I wanted to use the same database connection and change the schema based on which controller the call is made. One possible solution would be to pass the schema name from the controller layer to the service layer which in turn passes it to the dao layer. I want to avoid having two different data sources and then switching between them dynamically.
So is there any other better way that we could do this?
Note: We are using Mybatis as our persistence layer.
Please find the sample code :
Controller for ProductA
#Component
public class ProductA{
private final CommonService commonService;
public ProductA(CommonService commonService){
this.commonService = commonService;
}
#GET
#Path("/test/dbSchema")
#Produces(MediaType.APPLICATION_JSON)
public Response getTotalProducts(){
// Calling common service method here to get total products
}
}
Controller for ProductB
#Component
public class ProductB{
private final CommonService commonService;
public ProductA(CommonService commonService){
this.commonService = commonService;
}
#GET
#Path("/test/dbSchema")
#Produces(MediaType.APPLICATION_JSON)
public Response getTotalProducts(){
// Calling common service method here to get total products
}
}
FYI: We are using Jersey
Thank you #ave for the response. I am able to solve it by using ThreadLocal as you mentioned. By setting the tenant id in the controller, I am able to get the particular tenant in the common service.
Please find the updated code below:
Controller for Product A
#Component
public class ProductA{
private final CommonService commonService;
public ProductA(CommonService commonService){
this.commonService = commonService;
}
public void setSchema(){
TenantContext.setCurrentTenant("ProductA");}
#GET
#Path("/test/dbSchema")
#Produces(MediaType.APPLICATION_JSON)
public Response getTotalProducts(){
setSchema();
// Calling common service method here to get total products
}}
Controller for Product B
#Component
public class ProductB{
private final CommonService commonService;
public ProductA(CommonService commonService){
this.commonService = commonService;
}
public void setSchema(){
TenantContext.setCurrentTenant("ProductA");}
#GET
#Path("/test/dbSchema")
#Produces(MediaType.APPLICATION_JSON)
public Response getTotalProducts(){
setSchema();
// Calling common service method here to get total products
}
}
I have following issue: we've been developing website using Spring tools 3.8.3 and one of the option is to allow users to reset their passwords. In order to do that they need to specify their email address and validate captcha. When these steps are completed data is sent to ResetPassword controller which does all necessary checks and work. Controller has following address:
website.com/path/resetPassword/
And has following structure where it extends abstract controller template to define the input and output beans:
#RequestMapping(path="website.com/path/resetPassword/",
consumes = "application/JSON",
produces = "application/JSON",
method = "POST")
public class ResetPassController extends ControllerTemplate<ResetPassCaptchaBean, ResponseBean>{
// Autowired field declaration
// Couple of methods calling services
}
The bean also has only two fields - email and captcha accordingly. Captcha field uses annotation to check if either it's valid or not:
public class ResetPassCaptchaBean extends ResetPassBean {
#CaptchaValid
private String captcha;
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
public String getCaptcha() {
return captcha;
}
}
public class ResetPassBean {
private String email;
public void setEmail(String email) {
this.email= email;
}
public String getCaptcha() {
return email;
}
}
Now we are adding mobile API and it's necessary to do the same procedure but without using captcha validation. Therefore we're simply using the bean without captcha field. Since captcha is validated using the annotation in the bean it's completely not used in the controller itself and therefore we'd like to have the same controller both for handling requests from web site and from mobile API.
I understand that it's possible to create two different controllers and move all the implementation to service layer (read other class) but then there still would be two identical classes with only one line difference
public class ResetPassController extends ControllerTemplate<ResetPassCaptchaBean, ResponseBean>{
//Autowiring same stuff
//Calling same methods
}
and
public class MobileResetPassController extends ControllerTemplate<ResetPassBean, ResponseBean>{
//Autowiring stuff
//Calling same methods
}
So the question is: leave it with two identical controllers or is there other solution?
Thanks!
I'm relativity new to IOC and DI, so I'm guessing that I am missing some high-level design principle here, but I cannot figure out how to get my architecture working.
I have a REST API endpoint that accepts two pieces of POST data: customer ID, and Type ID. The rest api then needs to return a set of data for that specific customer/type combo.
Here is a crude picture of what I am doing:
The controller is taking the entity IDs passed in via post data, and via a JPA repository getting the appropriate Entities for them.
I then construct a data generator object (that takes the entities as constructor parameters), and use that to handle all of the data gathering for the API.
The Problem: because the Data Generator takes the two dynamic constructor parameters, it cannot be DI'ed into the Controller, but instead must be made with new. Inside of the Data Generator, however, I need access to JPA repositories. The only way to get access to these repositories is via DI. I cannot DI however, as the object was new'ed not DI'ed by the IOC container.
Is there a way to architect this so that I don't have this problem? Am I breaking some rule regarding IOC? Do I have wrong assumptions somewhere? Any advice is appreciated.
Thanks!
Edit: Pseudo code for Data Generator
public class DataGenerator {
private Customer customer;
private Type type
public DataGenerator(Customer customer, Type type) {
this.cusomter = customer;
this.type = type;
}
public generateData() {
if(customer == x && type == y) {
//JPA REPOSITORY QUERY
} else {
//DIFFERENT JPA REPOSITORY QUERY
}
}
}
I think you may have gotten confused somewhere along the line. You should have a Service that hits your repositories, and provide the information to the controller. One crude setup would be something like this.
#Controller
public MyController {
#AutoWired
private DataService dataService;
#RequestMapping(value = "/", method = RequestMethod.GET)
private DataGenerator readBookmark(#PathVariable Long customerId, #PathVariable Integer typeId) {
return dataService.getData(customerId, typeId);
}
}
#Service
public class DataService {
#AutoWired
private JPARepository repository;
public DataGenerator getData(long customerId, int typeId) {
Type typeDetails = repository.getType(typeId);
Customer customerDetails = repository.getCustomer(customerId);
return new DataGenerator(customerDetails, typeDetails);
}
}
I did Google a lot to find my problem but I couldn't and sorry If this question already on the stack overflow because I have not find it.
First let take a look into the code
#Controller
public class Controller1 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
}
public int calculation(int i){
//Some Calcucation
return i;
}
}
and second controller is
#Controller
public class Controller2 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
//In this I want to call the calculation(1) method of controller1.
}
}
My question is that is there any way to call the method of calculation() of controler1 in to controller2. But remember I don't want to make method static in controller1.Is there anyway to call it without make it static?
Thanks
Yasir
You should create service bean for example in configuration file (or use # one of the annotaions) and inject it into controller. For example ()
#Configuration
public class MyConfig {
#Bean
public MyService myService(){
return new MyService();
}
}
#Controller
public class Controller1 {
#Autowire
private MyService myService;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
#Controller
public class Controller2 {
#Autowire
private MyBean myBean;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
Your controllers should not call each other. If there is a logic which needs to be used by both controllers, it is much better to put that into separate bean, which will be used by both controllers. Then you can simply inject that bean to whicheveer controller neccessary. Try not to put any business logic to controllers, try tu put it to specialized class instead which will be web independent if possible and will accept web agnostic business data as user email, account number etc. No http request or response. This way your class with actual logic is reusable and can be unit tested much more easily. Also, if there is state, it should be contained in your classes outside controllers. Controllers should be stateless and not contail any state at all.
When using MVC pattern and you are deciding where to put your logic, you should separate business logic into model and into controllers you should put only logic regarding user interaction, as explained in this stack overflow post.
I want to convert an existing web service to take advantage of spring-data-rest.
How can I implement custom logic with injected values (specifically an OAuth2 Principal) on top of spring data rest to keep existing functionality ?
For example, say I want to override the GET method for /person/1 to contact an auditing web service before it goes on to return the data of person 1.
Right now, before using spring-data-rest, I would have:
#RequestMapping(value = "/person/{id}", method = RequestMethod.GET)
public void getPerson(#RequestBody ...., Principal principal)
{
/* Let Big Brother know that principal.getName() is doing this */
return thePerson;
}
How would I do something like this with Spring Data Rest generated endpoints?
Thanks for the suggestions. I found out that what works best (for me) in this case is what Dave Syer suggested and to just go with AspectJ.
In the end this will allow you to add custom logging logic, or anything else really, to the methods of a Spring Data JPA Repository:
#Component
#Aspect
public class MyRepositoryAuditor {
// pointcut on all methods with any arguments inside MyRepository
#Pointcut("execution(public * example.jpa.repositories.MyRepository.*(..) )")
public void publicRepositoryMethod() {
}
// if they return normally
#AfterReturning("publicRepositoryMethod()")
public void publicRepositoryMethod(JoinPoint jp) throws Throwable {
String methodName = jp.getSignature().getName();
.... perform logging ....
}
}