I'm designing my REST application architecture using Domain Driven Design and Adapter patter (there are interfaces, and many implementations in the aggregate root). It's all fine as long as don't add HATEOAS to the puzzle. In HATEOAS my value objects (on the bottom of dependency hierarchy) need to depend on resources (in the top layer). This messes up everything. I'm fairly new to HATEOAS so maybe I'm missing something. I'm planning to use Dropwizard and Jersey Declarative Linking.
Here is a diagram of my architecture:
Little clarification - this "Return and attributes types" between interfaces and value objects should actually be "Return and argument types" - It means, that all the interfaces' methods take objects from Value objects module as an arguments and return those objects to the caller.
I can add a piece of code that will show you what's in what module:
REST - JAX-RS Resources
#Component
#Path("/groups")
#Produces(MediaType.APPLICATION_JSON)
public class GroupsResource {
#Autowired
ProcessEngine processEngine; //interface with driver implementation under it
#GET
#Timed
public List<UserGroup> getUserGroups(#Auth BpmUser user) {
return processEngine.getUserGroups(user.id);
}
}
Interface ProcessEngine
public interface ProcessEngine {
void init();
List<UserGroup> getUserGroups(String username);
}
Implementation in drivers module
public class ActivitiProcessEngine implements ProcessEngine {
private org.activiti.engine.ProcessEngine processEngine;
private DataSource dataSource;
private String databaseType;
public ActivitiProcessEngine(String databaseType, DataSource dataSource) {
this.databaseType = databaseType;
this.dataSource = dataSource;
}
#Override
public void init() {
if (processEngine != null)
throw new ProcessEngineAlreadyInitializedException();
try {
processEngine = createProcessEngineConfiguration().buildProcessEngine();
ProcessEngines.registerProcessEngine(processEngine);
} catch (SQLException e) {
throw new ProcessEngineDatabaseException(e);
}
}
#Override
public List<UserGroup> getUserGroups(String username) {
return processEngine
.getIdentityService()
.createGroupQuery()
.groupMember(username)
.list()
.stream()
.map(Group::getId)
.map(UserGroup::new)
.collect(Collectors.toList());
}
...
}
Value object
public class UserGroup {
#JsonProperty
public String name;
//I want to be able add linking to another resources here
public UserGroup(String name){
this.name = name;
}
}
Domain object should never know anything about Controller or any other application logic. So, link controllers to domain object. It will solve your dependency problem.
Related
Background:
I am working on a java Spring REST microservice that needs to work with multiple identical back-end systems and multiple identical databases depending on the request parameters.
Basically I have 3 "brands". For each brand there is a set of downstream services and a database. I have no control over those.
My spring service will receive brand as a part of request and will need to call the right downstream services and use the correct database.
Previously I would deal with this by having a separate instance of the spring service for each of the brands. There would be a single property file for each brand and spring would use it to wire up beans. I would have separate URL's for each brand and there was no problem.
Some of my beans need to know about "brand" during creation as they are wrappers around connections downstream services. I.e. once the bean is created there won't be a way to switch it to be a "different brand".
Problem:
I would like to change this so that a single instance of my service can handle requests for any brand.
Requirements:
I was thinking about the following solution:
Have a general property file for non-branded stuff. Spring would wire any non-branded beans and keep them as singleton beans.
Have a property file with brand specific urls etc for each of the brands
Spring would create set of singleton beans for each of the brand using appropriate property file.
Next when the request comes in spring would read the request params and use bean specific for that brand.
Performance is important to me so I would like to reuse the beans as much as possible.
I would like to make this thing as transparent as possible so that people creating new beans don't have to worry about doing anything outside standard configuration/context class.
Does anyone know what would be the best solution to achieve this?
I think you can solve the problem injecting the service in every request with the right set of configurations and beans; possibly already existing in your Application Context.
Given:
$ curl http://localhost:8080/greetings/rodo && echo
Hi from brand1, rodo
$ curl -H "x-brand-name: brand1" http://localhost:8080/greetings/rodo
Hi from brand1, rodo
$ curl -H "x-brand-name: brand2" http://localhost:8080/greetings/rodo && echo
Hi from brand2, rodo
The following code would work:
-- application.yml --
brand1:
greetingPrefix: Hi from brand1,
brand2:
greetingPrefix: Hi from brand2,
-- DemoApplication.java --
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Configuration
class ServiceConfig {
#Bean
public GreetingService greetingServiceBrand1(Brand1Config config) {
return new GreetingService(config);
}
#Bean
public GreetingService greetingServiceBrand2(Brand2Config config) {
return new GreetingService(config);
}
}
#Configuration
class WebConfig implements WebMvcConfigurer {
#Autowired
private ApplicationContext applicationContext;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(greetingServiceResolver());
}
private GreetingServiceResolver greetingServiceResolver() {
GreetingService greetingServiceBrand1 = applicationContext.getBean("greetingServiceBrand1", GreetingService.class);
GreetingService greetingServiceBrand2 = applicationContext.getBean("greetingServiceBrand2", GreetingService.class);
return new GreetingServiceResolver(greetingServiceBrand1, greetingServiceBrand2);
}
}
}
#RestController
#RequestMapping("/greetings")
class GreetingController {
#GetMapping("/{name}")
public String get(GreetingService greetingService, #PathVariable String name) {
return greetingService.sayHi(name);
}
}
class GreetingServiceResolver implements HandlerMethodArgumentResolver {
private final GreetingService greetingServiceBrand1;
private final GreetingService greetingServiceBrand2;
public GreetingServiceResolver(GreetingService greetingServiceBrand1, GreetingService greetingServiceBrand2) {
this.greetingServiceBrand1 = greetingServiceBrand1;
this.greetingServiceBrand2 = greetingServiceBrand2;
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(GreetingService.class);
}
#Override
public Object resolveArgument(
MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory
) throws Exception {
String brand = nativeWebRequest.getHeader("x-brand-name");
return resolveGreetingService(brand);
}
private GreetingService resolveGreetingService(String brand) {
if ("brand2".equals(brand)) {
return greetingServiceBrand2;
}
return greetingServiceBrand1; // default
}
}
class GreetingService {
private BaseConfig config;
public GreetingService(BaseConfig config) {
this.config = config;
}
public String sayHi(String name) {
return config.getGreetingPrefix() + " " + name;
}
}
abstract class BaseConfig {
private String greetingPrefix;
public String getGreetingPrefix() {
return greetingPrefix;
}
public void setGreetingPrefix(String greetingPrefix) {
this.greetingPrefix = greetingPrefix;
}
}
#Configuration
#ConfigurationProperties("brand1")
class Brand1Config extends BaseConfig {
}
#Configuration
#ConfigurationProperties("brand2")
class Brand2Config extends BaseConfig {
}
As you can see, it's fundamental to pass the service to each controller method, write a resolver and inject the right set of dependencies depending on a parameter passed to the request, in this case via header.
Since your property files need to be declared statically anyway, you can just write all your different brand stuff in the same property file, like in a key-value format, that Spring can pick up as a list of configurations.
brandConfigs:
- brand: foo
property: foos
- brand2: bar
porperty: bars
Load all your connection beans to your downstream services on startup and just route to them according to your request param. Imo this seems to be the most straight forward and performant way. If some of these downstreams are used very rarely you can lazy load the beans on-demand, but probably this wouldn't make a sense unless you have thousands of different downstream routes.
So far, the only way I know to set the name of a database, to use with Spring Data ArangoDB, is by hardcoding it in a database() method while extending AbstractArangoConfiguration, like so:
#Configuration
#EnableArangoRepositories(basePackages = { "com.company.mypackage" })
public class MyConfiguration extends AbstractArangoConfiguration {
#Override
public ArangoDB.Builder arango() {
return new ArangoDB.Builder();
}
#Override
public String database() {
// Name of the database to be used
return "example-database";
}
}
What if I'd like to implement multi-tenancy, where each tenant has data in a separate database and use e.g. a subdomain to determine which database name should be used?
Can the database used by Spring Data ArangoDB be determined at runtime, dynamically?
This question is related to the discussion here: Manage multi-tenancy ArangoDB connection - but is Spring Data ArangoDB specific.
Turns out this is delightfully simple: Just change the ArangoConfiguration database() method #Override to return a Spring Expression (SpEL):
#Override
public String database() {
return "#{tenantProvider.getDatabaseName()}";
}
which in this example references a TenantProvider #Component which can be implemented like so:
#Component
public class TenantProvider {
private final ThreadLocal<String> databaseName;
public TenantProvider() {
super();
databaseName = new ThreadLocal<>();
}
public String getDatabaseName() {
return databaseName.get();
}
public void setDatabaseName(final String databaseName) {
this.databaseName.set(databaseName);
}
}
This component can then be #Autowired wherever in your code to set the database name, such as in a servlet filter, or in my case in an Apache Camel route Processor and in database service methods.
P.s. I became aware of this possibility by reading the ArangoTemplate code and a Spring Expression support documentation section
(via), and one merged pull request.
We are working on a multilingual Spring based web application (not Spring Boot).
Now we are searching for the "spring way" of doing the following.
user starts a http session with some parameters, e.g. a locale "de" and/or a country-code "DE" (the type of parameter isn't really important)
user works with application
at some point the user triggers an action that somewhere deep inside needs a "localized" functionality
Example (java pseudocode):
// service with functionality common for all users
#Service
class CommonService implements ICommonService
{
// how to autowire a service based on some info in the actual HttpSession, eg. CustomServiceUK or CustomServiceDE
#Autowired
private ICustomService customServiceImpl;
#Override
public void doSomeAction(String param)
{
... do some common stuff
customResult = customServiceImpl.calculate(param);
... do some common stuff with custom result
}
}
// custom service implementations
#Service("CustomServiceUK")
class CustomServiceUK implements ICustomService
{
#Override
public String calculate(String value)
{
... execute logic on value for an "uk" user
}
}
#Service("CustomServiceDE")
class CustomServiceDE implements ICustomService
{
#Override
public String calculate(String value)
{
... execute logic on value for an "de" user
}
}
How to inject a custom service based on some info in the actual HttpSession (e.g. CustomServiceUK or CustomServiceDE) into CommonService?
What are our options to solve this issue? Is there something like a dynamic #Qualifier or some #Autowired Spring-Factory thing?
(the service implementation to use must not necessarily depend on the locale of the user but on some other piece of session/request information)
Thanks for your answers.
Actually we end up with the following solution which works for us.
We created an additional implementation of ICustomService with name CustomServiceProxy.
This service has #Primary annotation to tell Spring that this component should be injected when no explicit qualifier is supplied.
The service gets the sessionData and a Map with all Spring managed ICustomService-Components injected (Map-Key = Qualifier of the Component).
Now when some method on CustomServiceProxy gets called, it generates the Map-Key based on the actual sessionData (e.g. language), lookup the ICustomService in the Map and delegates the call to this specific service.
// service with functionality common for all users
#Service
class CommonService implements ICommonService
{
// because of #Primary an instance of CustomServiceProxy will be injected
#Autowired
private ICustomService customServiceImpl;
#Override
public void doSomeAction(String param)
{
... do some common stuff
customResult = customServiceImpl.calculate(param);
... do some common stuff with custom result
}
}
// custom service implementations
#Service
#Primary
class CustomServiceProxy implements ICustomService
{
private CustomData sessionData;
private Map<String, ICustomService> services;
#Autowired
public CustomServiceProxy(CustomData sessionData, Map<String, ICustomService> services)
{
this.sessionData = sessionData;
this.services = services;
}
#Override
public String calculate(String value)
{
String serviceName = "CustomService" + sessionData.getLanguage().toUpperCase();
ICustomService customService = services.get(serviceName);
// handle missing service: throw exception or maybe switch to a default implementation
Objects.requireNonNull(customService, "missing CustomService with name " + serviceName);
return customService.calculate(value);
}
}
#Service("CustomServiceUK")
class CustomServiceUK implements ICustomService
{
#Override
public String calculate(String value)
{
... execute logic on value for an "uk" user
}
}
#Service("CustomServiceDE")
class CustomServiceDE implements ICustomService
{
#Override
public String calculate(String value)
{
... execute logic on value for an "de" user
}
}
While working with a project that involves requesting multiple data types from a database I came to a following question:
Lets say I have 2 java classes that correspond to database entities:
Routes
public class Route {
public Route(int n, int region, Date fdate, boolean changed, int points,
int length) {
super();
this.n = n;
this.region = region;
this.fdate = fdate;
this.changed = changed;
this.points = points;
this.length = length;
}
}
Carrier
public class Carrier {
public Carrier(...) {
this.id = src.getId();
this.name = src.getName();
this.instId = src.getInstId();
this.depotId = src.getDepotId();
}
If so, what's the correct approach of creating Dao interfaces and classes? I'm doing it like this -
#Repository
public class CarrierDaoImpl implements CarrierDao{
#Autowired
DataSource dataSource;
public List<Carrier> getAllOrgs() { ... }
}
#Repository
public class RoutesDaoImpl implements RoutesDao {
#Autowired
DataSource dataSource;
public ArrayList<AtmRouteItem> getRoutes(AtmRouteFilter filter) { ... }
}
I'm creating a #Repository DAO for every java class item\db entity and then 2 separate controllers for requests about carriers and routes. Like this:
#RestController
#RequestMapping(path = "/routes")
public class RoutesController {
#Autowired
RoutesDao routesDao;
#GetMapping(value = {"/getRoutes/", "/getRoutes"})
public ArrayList<Route> getRoutes() { ... } }
And same for controller Carriers. Is it correct and if not what's the correct approach?
Sorry for styling issues, that's my first question on stackoverflow :)
I would suggest creating services marked with #Service annotation (i.e. CarrierService interface and CarrierServiceImpl implementation). Than inject them into controllers. Use repositories within services because some database operations will require transactions and a better place for managing transactions are services. Also services can do more specialized job which will require access to multiple repositories so you can inject them. And don’t forget to mark your services with #Transactional annotation.
It's correct to have a DAO for each entity.
When working with JPA repositories you have no choice but to provide the entity. For instance:
public interface FooRepository extends JpaRepository<Foo,Long>{}
Same for the REST controllers, you have to bring together functionalities by object as you do.
You can improve your mapping to be more RESTful. To retrieve all routes, don't specify a path:
#GetMapping
public ArrayList<RouteResource> getRoutes() { ... }
(I never use #GetMapping yet but it should work like that)
And if you want specific route:
#GetMapping("/get/{id}")
public RouteResource getRoute() {...}
You should return resources instead of entities to client.
I have a system (Java with Spring Framework) that exposes 7 different Apache Thrift servlets over HTTP using the TServlet class. Currently they all need their own Servlets, ServletMappings, Processors, Handlers etc. so implementing clients have to also keep an internal list of all the various URLs for the different services.
I understand that Apache Thrift supports multiplexing when using TServer and its derivatives by using TMultiplexingProcessor, however since I am using Spring and my Servlet, Handler and Processor are all Spring Beans that get autowired into one another, I'm unsure how to proceed.
Here's an example of how one of the services gets wired up:
UserServiceHandler.java
#Component
public class UserServiceHandler implements UserService.Iface {
#Override
public User getUser(String userId) throws TException {
// implementation logic goes here
}
}
UserServiceProcessor.java
#Component
public class UserServiceProcessor extends UserService.Processor<UserServiceHandler> {
private UserServiceHandler handler;
#Autowired
public UserServiceProcessor(UserServiceHandler iface) {
super(iface);
handler = iface;
}
public UserServiceHandler getHandler() {
return handler;
}
public void setHandler(UserServiceHandler handler) {
this.handler = handler;
}
}
UserServiceServlet.java
#Component
public class UserServiceServlet extends TServlet {
private UserServiceProcessor processor;
#Autowired
public UserServiceServlet(UserServiceProcessor p) {
super(p, new TBinaryProtocol.Factory());
processor = p;
}
}
Servlet Registration
ServletRegistration.Dynamic userService = servletContext.addServlet("UserServiceServlet", (UserServiceServlet) ctx.getBean("userServiceServlet"));
userService.setLoadOnStartup(1);
userService.addMapping("/api/UserService/*");
// This same block repeated 7 times for each *ServiceServlet with different mappings
I would like to have all 7 service handlers map to a single URL like /api/*. Is this even possible? I suppose I would have to create a single servlet and processor, but I'm unsure what they should look like. My processors extend UserService.Processor and the like.
OK, figured it out. Might not be the best way, so I welcome criticism.
Here were my rough steps:
Keep the handler classes the way they were.
Create a new class that extends TMultiplexedProcessor
Create a new class that extends TServlet
All Processors (e.g. the UserServiceProcessor have a handler property and a corresponding getter and setter
Here is my ApiMultiplexingProcessor:
#Component
public class ApiMultiplexingProcessor extends TMultiplexedProcessor {
UserServiceHandler userServiceHandler;
ReportServiceHandler reportServiceHandler;
// ... more service handlers can go here
#Autowired
public ApiMultiplexingProcessor(UserServiceProcessor userServiceProcessor, ReportServiceProcessor reportServiceProcessor) {
this.registerProcessor("UserService", userServiceProcessor);
this.registerProcessor("ReportService", reportServiceProcessor);
// add more registerProcessor lines here for additional services
userServiceHandler = userServiceProcessor.getHandler();
reportServiceHandler = reportServiceProcessor.getHandler();
// set any additional service handlers here
}
// getters and setters for the handlers
public UserServiceHandler getUserServiceHandler() {
return userServiceHandler;
}
public void setUserServiceHandler(UserServiceHandler userServiceHandler) {
this.userServiceHandler = userServiceHandler;
}
public ReportServiceHandler getReportServiceHandler() {
return reportServiceHandler;
}
public void setReportServiceHandler(ReportServiceHandler reportServiceHandler) {
this.reportServiceHandler = reportServiceHandler;
}
}
So to explain the above a bit, if you add any additional services, you need to add the *ServiceHandler classes as fields on this class, and create the getters and setters etc.
So now that we have that, we can create a new single servlet that will be added to the servlet context.
Here is my ApiServlet:
#Component
public class ApiServlet extends TServlet {
private ApiMultiplexingProcessor processor;
#Autowired
public ApiServlet(ApiMultiplexingProcessor p) {
super(p, new TBinaryProtocol.Factory());
processor = p;
}
}
And then you just add this servlet to the servlet context (from a bean) as before:
ServletRegistration.Dynamic api = servletContext.addServlet("ApiServlet", (ApiServlet) ctx.getBean("apiServlet"));
api.setLoadOnStartup(1);
api.addMapping("/api/*");
// yay now we have a single URL and a single servlet
This all could be helpful to someone else in my situation, so enjoy!
P.S. make sure when adapting your clients you use the TMultiplexedProtocol so that you can pass the service name through when talking to the server e.g.
TTransport transport = new THttpClient(new Uri("https://myapp.com/api/"));
TProtocol protocol = new TBinaryProtocol(transport);
TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "UserService");
UserService.Client userServiceClient = new UserService.Client(mp);