Say I have 5 different microservices. Each use one common DTO, that is UserDTO.java or an entity User.java.
If these DTOs are to be accessed by all, I came up with two approaches
To place all these DTOs in all the microservices
To create a project say Project-commons and keep all the common DTOs there and then add the dependency of this project in all microservices
I know that the sharing common DTOs across microservices would kill the concept/principles of microservices, but I wanted to know is there any other approaches available at all?
Actually, sharing a common code library does not kill the concept/principle of keeping changes isolated to one microservice, as long as you strictly version that library. That way, one microservice team can decide it needs new functionality in the shared library, and can bump the version number of the library and add the new functionality. Existing microservices that use that library won't be affected until they independently decide to start using the newer version of that library.
So this is what we do. We have jar files that we share across our microservices, but we push specific versions of those jar files to Nexus and reference those jar files by version number in each microservice.
Note that this concept is no different than two microservices depending on a third party component, like an Apache Commons library. Just because Apache comes out with a new version of a library doesn't mean anyone's binaries change. Each codebase that depends on that library can decide if and when it moves to the new version independent of what version another codebase might be using or what the most recent version of the library might be.
Related
I am designing the architecture of my new app.I chose microservice architecture.In my architecture I noticed that I have models that are used by diffrent microservices. I want to know if there is a way to share models code between microservices instaed of writing them in each microservice.
By the way I am using the spring boot framework for my app.
You should only be sharing models that define the API of your micro-service e.g. Protobuff .proto files or the Java classes generated from them.
This is normally done by creating either a separate project or converting your micro-service projects into a multi-module projects where one of the modules is a thin API module with interface definition.
There is nothing wrong in sharing code between micro-services but you have to be careful. Share too much internal implementation details and you end up with a distributed monolith instead of micro-services.
You can create a separate project with common models, create a jar of this project and add dependency of this jar in other microservices.
But I have a practical experience, its a nightmare to maintain this common project, because for every change you have to create a new version and update the build scripts of all the microservices.
In my opinion we should not share models among the microservices.
In a Microservices architecture, each one is absolutely independent of the others and it must hide the details of the internal implementation.
If you share the model you are coupling microservices and lose one of the greatest advantages in which each team can develop its microservice without restrictions and the need of knowing how evolve others microservices. Remember that you can even use different languages in each one, this would be difficult if you start to couple microservices.
https://softwareengineering.stackexchange.com/questions/290922/shared-domain-model-between-different-microservices
If you are draconian about this decision you will run into unsatisfactory conditions one way or the other. It depends on your SDLC and team dynamics. At Ipswitch, we have many services that all collaborate and there are highly shared concepts like device and monitor. Having each service have its own model for those would be unsustainable. We did that in one case and the translation just created extra work and introduced inconsistency defects. But that whole system is built together and by one large dev team. So sharing makes the most sense there. But for an enterprise, where you have multiple teams and multiple SDLCs across microservices, it makes more sense to isolate the models to avoid coupling. Even then, however, a set of closely collaborating services that are managed by a given team can certainly share a model if the team accepts the risk/benefit of doing so. There is nothing wrong with that beyond academics and philosophy.
So in short, share minimally but also avoid unnecessary work for your team.
You could move your model classes to a different project/repository and add it as a dependency to your microservices that need to share it.
Not sure if your microservices use Swagger, but, you can use Swagger Codegen to generate your models.
For example, If you have UserService which accepts and/or returns User object. The consumer of UserService can use the Swagger Codegen plugin to auto-generate the User class at build time.
You can use Swagger Codengen maven or gradle plugin pretty easily.
Just as shown in the picture, one app (Java) referenced two third-party package jars (packageA and packageB), and they referenced packageC-0.1 and packageC-0.2 respectively. It would work well if packageC-0.2 was compatible with packageC-0.1. However sometimes packageA used something that could not be supported in packageC-0.2 and Maven can only use the latest version of a jar. This issue is also known as "Jar Hell".
It would be difficult in practice to rewrite package A or force its developers to update packageC to 0.2.
How do you tackle with these problems? This often happens in large-scale companies.
I have to declare that this problem is mostly occurred in BIG companies due to the fact that big company has a lot of departments and it would be very expensive to let the whole company update one dependency each time certain developers use new features of new version of some dependency jars. And this is not big deal in small companies.
Any response will be highly appreciated.
Let me throw away a brick in order to get a gem first.
Alibaba is one of the largest E-Commerces in the world. And we tackle with these problems by creating an isolation container named Pandora. Its principle is simple: packaging those middle-wares together and load them with different ClassLoaders so that they can work well together even they referenced same packages with different versions. But this need a runtime environment provided by Pandora which is running as a tomcat process. I have to admit that this is a heavy plan. Pandora is developed based on a fact that JVM identifies one class by class-loader plus classname.
If you know someone maybe know the answers, share the link with him/her.
We are a large company and we have this problem a lot. We have large dependency trees that over several developer groups. What we do:
We manage versions by BOMs (lists of Maven dependencyManagement) of "recommended versions" that are published by the maintainers of the jars. This way, we make sure that recent versions of the artifacts are used.
We try to reduce the large dependency trees by separating the functionality that is used inside a developer group from the one that they offer to other groups.
But I admit that we are still trying to find better strategies. Let me also mention that using "microservices" is a strategy against this problem, but in many cases it is not a valid strategy for us (mainly because we could not have global transactions on databases any more).
This is a common problem in the java world.
Your best options are to regularly maintain and update dependencies of both packageA and packageB.
If you have control over those applications - make time to do it. If you don't have control, demand that the vendor or author make regular updates.
If both packageA and packageB are used internally, you can use the following practise: have all internal projects in your company refer to a parent in the maven pom.xml that defines "up to date" versions of commonly used third party libraries.
For example:
<framework.jersey>2.27</framework.jersey>
<framework.spring>4.3.18.RELEASE</framework.spring>
<framework.spring.security>4.2.7.RELEASE</framework.spring.security>
Therefore, if your project "A" uses spring, if they use the latest version of your company's "parent" pom, they should both use 4.3.18.RELEASE.
When a new version of spring is released and desirable, you update your company's parent pom, and force all other projects to use that latest version.
This will solve many of these dependency mismatch issues.
Don't worry, it's common in the java world, you're not alone. Just google "jar hell" and you can understand the issue in the broader context.
By the way mvn dependency:tree is your friend for isolating these dependency problems.
I agree with the answer of #JF Meier ,In Maven multi-module project, the dependency management node is usually defined in the parent POM file when doing unified version management. The content of dependencies node declared by the node class is about the resource version of unified definition. The resources in the directly defined dependencies node need not be introduced into the version phase. The contents of the customs are as follows:
in the parent pom
<dependencyManagement>
<dependencies >
<dependency >
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-model</artifactId>
<version>${project.version}</version>
</dependency >
</dependencies >
</dependencyManagement>
in your module ,you do not need to set the version
<dependencies >
<dependency >
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-model</artifactId>
</dependency >
</dependencies >
This will avoid the problem of inconsistency .
This question can't be answered in general.
In the past we usually just didn't use dependencies of different versions. If the version was changed, team-/company-wide refactoring was necessary. I doubt it is possible with most build tools.
But to answer your question..
Simple answer: Don't use two versions of one dependency within one compilation unit (usually a module)
But if you really have to do this, you could write a wrapper module that references to the legacy version of the library.
But my personal opinion is that within one module there should not be the need for these constructs because "one module" should be relatively small to be manageable. Otherwise it might be a strong indicator that the project could use some modularization refactoring. However, I know very well that some projects of "large-scale companies" can be a huge mess where no 'good' option is available. I guess you are talking about a situation where packageA is owned by a different team than packageB... and this is generally a very bad design decision due to the lack of separation and inherent dependency problems.
First of all, try to avoid the problem. As mentioned in #Henry's comment, don't use 3rd party libraries for trivial tasks.
However, we all use libraries. And sometimes we end up with the problem you describe, where we need two different versions of the same library. If library 'C' has removed and added some APIs between the two versions, and the removed APIs are needed by 'A', while 'B' needs the new ones, you have an issue.
In my company, we run our Java code inside an OSGi container. Using OSGi, you can modularize your code in "bundles", which are jar files with some special directives in their manifest file. Each bundle jar has its own classloader, so two bundles can use different versions of the same library. In your example, you could split your application code that uses 'packageA' into one bundle, and the code that uses 'packageB' in another. The two bundles can call each others APIs, and it will all work fine as long as your bundles do not use 'packageC' classes in the signature of the methods used by the other bundle (known as API leakage).
To get started with OSGi, you can e.g. take a look at OSGi enRoute.
Let me throw away a brick in order to get a gem first.
Alibaba is one of the largest E-Commerces in the world. And we tackle with these problems by creating an isolation container named Pandora. Its principle is simple: packaging those middle-wares together and load them with different ClassLoaders so that they can work well together even they referenced same packages with different versions. But this need a runtime environment provided by Pandora which is running as a tomcat process. I have to admit that this is a heavy plan.
Pandora is developed based on a fact that JVM identifies one class by class-loader plus classname.
I have a task that includes migrating an API Gateway from Zuul to Spring Cloud Gateway. There are two main versions currently: 1.0.1.RELEASE and 2.0.0.RC1. The first version is very basic and I'd have to manually implement filters related to rate limiting, authentication, etc...
While the second version has all the features we need with complete YML support. We have a strict rule in the company to never use beta or RC, and we need the first version of the gateway to be in production within a couple of weeks so there is not enough time to wait for the final release of version 2.
My team-leader specifically asked me to make 2 versions of using version 1.0.1 and 2.0.0 of SCG. How do you implement the module for maximum reusability? I mean I want switching between the two versions to be as easy as possible and I want to reuse as much of the logic as I can. The first thing that came to my mind is simply to create two separate projects. What do you think?
As I understand the question, you want an easy transition from the version 1.0.1.RELEASE to 2.0.0.RC1 of some dependency.
I would approach it as follows:
Create 3 modules (or projects):
api
bindings-1
bindings-2
The api module contains the API which you'll define to access functions of the dependency.
The bindings-1 and bindings-2 both implement what's defined in api, but based on the versions 1.0.1.RELEASE and 2.0.0.RC2 accordingly.
Your code will use the dependency only and exclusively via the api. No direct access to the classes and methods provided by the dependency. I would even not include the dependency as a compile-time dependency. You'll then import bindings-1 or bindings-2 depending on which version you want to use.
Having a separate api will require certain effort. It will seem overengineered. But if you don't do this, bindings to the dependency will diffuse in your code and switching from one version to another will be much more difficult.
With a dedicated api you will be forced to crystallize everything you need from the dependency in your api - in a version-independent manner.
I would also not develop bindings-1/bindings-2 as SCM branches. It's not like you'll be merging them, so why branches?
We have to develop and maintain many Java web based applications (for the same company) of different sizes, scopes and life-spans. Some of them are huge and other ones are just simple pages that may live only a few months (or days), some are already implemented and need refactoring.
There have one thing in common though, they need access to (almost) the same information.
Problem
Due to the complexity of the data the company handles, we have to deal with many different sources, some of them inherited from the ancient times. Our domain objects may be mapped across many of those sources. As an example, a Contract domain object is mapped to our main database but its related (physical) files are stored in a document server, and the activity related to it is stored in a NoSQL database. Therefore, adding, removing, searching any of these objects involves many internal operations.
Our data sources are (although it could be any):
AS400 (using DB2 as a database)
Documentum document manager
Mongo DB
External web services
Other legacy sources
We normally use Glassfish as the application server and maven as our build tool.
Goal
Our goal is to create a business layer or library that all of our applications can access and it is:
Compact
Consistant
Easy to use
Easy to maintain
Accessible from many different clients
What we have found so far
We have been struggling for weeks and still we cannot find anything fully satisfactory. Some solutions:
Pack all the business logic in one or more jars: Very easy to share, but all the applications will have to contain all the jar dependencies and configuration files and take care of security, caching and other stuff. Difficult to maintain (we have to update the jars for every project when there are changes).
Create an Ejb project containing all the logic and access it remotely: Easy to maintain, security, caching and configuration only implemented once. We are afraid of the penalty of the remote calls. As we have noticed in our research, it seems to be a bad practice (we don't have much experience with ejbs).
Create an Ear project with everything inside and use local access: Well, this is faster than the remote version but it is a hell to maintain.
Go for OSGI: We are a bit afraid of this one since it is not as popular as Ejb and we have never used it seriously.
Is there a common practice for this kind of problem?
Many thanks!
I would not recommend put all logic into 1 EAR project and use local access. If you have a lot of code in the one place, it will be harder to maintain, test, deploy etc.
I would create mutlti-module maven project with common dependencies. One of the dependency - service with business logic and DAO access, which will expose API. With Maven project you can easy control version of the POM files. Different projects may work with different version of common service. Maven will handle version control for you. However it's require some configuration and implementation efforts.
Another option mentioned by you - standalone EAR with remote EJBs should work fine as well. Do not worry about performance and number of remote calls, unless you have heavy load. Simply cache remote EJB stubs on client to avoid unnecessary JNDI lookup.
Personally I prefer first option with shared dependency managed by Maven. It's clear and easy to maintain, easy to manage versions, deploy, configure. With Maven you don't need to change jar file manually for every project, you can simply use tools like Nexus
I have a large scale project I am working on at the moment using Eclipse. Normally, as a one man team, these problems would not be an issue, but as our team is not one person we need to be able to break up pieces of the project to be worked on by certain team members.
In simplicity, let's say I have two layers to be separated apart:
1. Each DAO is a separate Java project, to be worked upon individually
2. The web-tier service layer contains all of our service endpoints and must be able to reference all of the DAOs. This layer runs on Tomcat as a dynamic web project, and utilizes Adobe LiveCycle Data Services as the piece that handles creation and management of endpoints.
Now, the issue we are running into is that when we create a DAO and unit test it individually it runs great. But when we reference it into our service project and try to run it we begin to get all kinds of issues related to the fact that we have two different versions of certain jars referenced in and as such we begin to have errors when running the server.
As a result, we know we can solve the issue by pulling the problem jars and ensuring that this is not an issue again in the future, but as I said before this is a large scale project with multiple people working on it and we don't want to be spending our time weeding out dependency issues when under the gun.
We are looking for recommendations on where to proceed for alternative solutions? Our team is new to JavaEE and as such we don't have much of a bearing on what we can use to tie everything together in it, or if it is a viable solution. Should we be looking at turning our DAOs into EJBs and deploying them in an EAR library? If so, where would our service layer lie, and would the service layer be able to reference the DAO classes since the EJB maintains it's own classpath (from what we have read?) Are we looking down the wrong path, or are we completely wrong in our current understanding of JavaEE?
Any assistance is greatly appreciated. We are still in the framework stage of this project and we want to be sure that we will be able to maintain it in the long run.
I second the Maven recommendation. That can add all sorts of sanity to your project structure.
Maven can even generate Eclipse workspaces via mvn eclipse:eclipse
An important clarification on the EJBs note. As of ava EE 6 is you no longer need to separate EJBs from Servlets and can use them together in the very same jar in the war file.
So understand from that that using EJBs or not no longer has any impact on packaging or classloaders as it once did. These are now separate decisions. EARs and classloader separation should now be viewed as a feature you might want to use should you want classloader separation and the complexity it brings. Most applications simply do not need that and are more than fine with just a war file containing servlets, ejbs, jpa entities, cdi beans, jaxrs services and whatever else you need. You are free to decide how you want to separate them or if you want to bother separating them at all.
EJBs do make great DAOs due to transaction management, something you don't get from plain Tomcat but can be made available in Tomcat via TomEE and works fine in Eclipse. You should consider EJBs for that reason, not for dependency reasons.
Side note, as you're new to Java EE, you might find this helpful:
http://openejb.apache.org/examples-trunk/index.html
In order to have things organized when working with Java EE in teams of 1+ people I could suggest:
Use Maven to manage your build process and library dependencies.
Maven has a small learning curve, but once you grasp it you will be grateful. By using Maven you no longer depends on Eclipse to manage your classpath.
A thing about it that I think is really helpful when working in teams is the install feature. Suppose you are woking on the version 1.0 of an EJB module, say core-ejb-module-1.0, and you've got it to a stable state and want everyone working in the project to refer to it from now on.
You then run a maven command like this on it: mvn clean package install
Maven will clean this module, compile it, run tests, create the jar and then install it to a repository that you define. Could be any computer in your company.
Now you may tell the guys working on other projects to update this dependency version on their .pom file and in the next build they run, before compiling, maven will download this library and then use it. Really neat. No more classpath hell.
(There are other ways to always automatically refer to the latest library as stated in this post, but there are some caveats. Anyway it's just an example.)
Use JPA/EJB instead of DAO Pattern.
Some people say DAO meaning any sort of data access, others really mean that they use the DAO Pattern to access objects. If that is your case, you no longer need to use it when using JPA. (At least for most common scenarios).
In my case, I have a generic EntityService which is capable of doing CRUD operations on any Entity and has a centralized query management. Then every EJB's that should perform database related operations may inject this guy and do its job.
As a suggestion, with Maven, you project could be organized as such:
core project structure
core (The pom root)
core-ejb-module (Includes all generic EJB's, like the EntityService for instance.)
core-jpa-module (Includes all JPA generic definitions, like Interfaces, MappedSuperclasses and such.)
core-jsf-module (Includes all JSF generic definitions, like abstract controllers, generic converters and wrappers for FacesContext, etc..)
Now that you have a core generic module setup, you could create:
an application structure
app (The pom root)
app-ear-module (Includes all other modules in this application. Shared jars goes in the ear /lib folder, so all other modules could reference to them.)
app-ejb-module-a (Includes EJB's for the business layer of your application. It uses the core-ejb-module)
app-ejb-module-b (You may have lots of ejb modules. You may even have a project that contains only ejb modules. Other apps will declare their dependency on them via Maven.)
app-jpa-module (Contains definitions for JPA Entities that represents you database tables. Depends on the core-jpa-module)
app-web-module (Holds the pages, Controllers and Converters for this application.)
I think you got the idea. Things tend to be loosely coupled and you may organize your projects as you like.
This is just a simple example to illustrate. I didn't explained a lot about Maven but if you're interested I think it will help you indeed.
I hope it gives you some ideas and may help you in any way.
[]'s
If you can run all the sub-components using the same set of dependencies, you may find it helpful to migrate to a Maven build.
With Maven, you can define a top-level project that manages all the 3rd party dependency versions in one place, so all modules are built, tested and deployed against the same library versions. You are also likely to find Maven a good fit for the multi-module approach you have adopted, as it ensures that a project is rebuilt correctly if one of its dependencies changes.
You would still be able to use dynamic web projects as before; Eclipse will automatically deploy the DAOs as part of the service project (IIRC you need to characterise the DAOs as utility modules).
If you do go down the EJB root, you are correct that each EAR will get its own class-loader, and can therefore use a varying set of dependencies. However, in your position I would tend to look at improving your dependency management first - it'll probably be cheaper and easier.