Spring cloud function architecture with multiple functions - java

I just started a new project where I'm going to use Java, Spring cloud functions and AWS Lambda.
It's my first time building a serverless application and I've been looking at different example projects and tutorials on how to get started.
However, the projects I've found have been so small that it's hard to understand how to map it to a real project.
As I understand it you build a jar file and upload it to AWS Lambda where you specify which function to run.
However, as the project grows, more and more functions that aren't even going to run (unreachable code) will make the jar bigger and bigger and cause each Lambda startup to be slower and slower?
I could create separate modules for each Lambda function with its own Application class in order to build separate jars, but it doesn't feel like the intended architecture.
Also, I would like to be able to run all of the functions locally using tomcat in a single application.
I guess I could build a separate module specifically designed to run locally, but again it doesn't feel like the intended architecture.
Any suggestions or references to best practices would be greatly appreciated.

TL;DR:
One JAR per function, not all functions in one JAR.
Use Maven modules. One module per Lambda function.
Don't run the Lambda locally, use unit tests with mocks.
Deploy to AWS to test if the Lambda works as intended.
Reading the question I get the feeling that there are a few misconceptions on how AWS Lambda works, that need to be addressed first.
However, as the project grows, more and more functions that aren't even going to run (unreachable code) will make the jar bigger and bigger [...]
You do not deploy a single JAR that contains all your Lambda functions. Every function is deployed as a single JAR. So if you have 20 Lambda functions, you deploy 20 JAR files.
The size of the JAR file is determined by the individual dependencies of the function. A function might use a specific dependency an another might not. So JAR size will differ depending on your dependencies.
One way to improve this is to split your code from the dependencies, by putting the dependencies in Lambda layers. This way, you only deploy a small JAR with your code. The dependency JAR should only be deployed, when the dependencies have been updated. Unfortunately, this will make deployments more complex, but it is doable.
I could create separate modules for each Lambda function with its own Application class in order to build separate jars, but it doesn't feel like the intended architecture.
That's what I'd recommend. And it is more or less the only way. AWS Lambda has a 1 to 1 relationship between the JAR and the function. One Lambda function, per JAR. If you need a second Lambda function, you need to create it and deploy another JAR.
Also, I would like to be able to run all of the functions locally using tomcat in a single application. I guess I could build a separate module specifically designed to run locally, but again it doesn't feel like the intended architecture.
There are tools to run Lambdas locally, like the serverless framework. But running all the Lambdas in a Tomcat is probably going to be hard work.
In general, running Lambdas locally is something I'd not recommend. Write unit tests to run the code locally and deploy to AWS to test the Lambda. There is not really any better way I can think of to do testing efficiently.
Most Lambdas communicate with other services, like DynamoDB, S3 or RDS. So how would you run those locally? There are options, but it just makes everything more and more complicated. And what about services that you can't easily emulate locally (EventBridge, IAM, etc.)? That's why in my experience, running serverless applications locally is unrealistic and will not give you confidence that they'll work once deployed. So why not deploy during development and test the "real" thing?

From my experience, I would recommend using multiple Maven modules, one function per Maven module. Create shared modules for common logic. This approach would require you to implement some smart deployment pipeline to tell which function must be deployed if you change a common lib shared between many functions. If you don't have shared modules using just a hash on /src might be enough, otherwise, you need to add some metadata that describes the relation between Maven modules. I haven't investigated it but it might be possible to get the relation between modules from Maven to feed in your CI/CD so you use build tool to help sort out CD
It's possible to keep all functions within the same JAR and deploy one Jar multiple times with different entry points. The downside is you have tight coupling between all functions. Changes for one function might have some side effects on the other functions. Also coupling all functions within one JAR might make your function slower as it would create one Spring context containing all different beans. Also, Spring Boot approach with autoconfiguration would not help when for one function you need DB connection configured and for another, you need messaging configured. Ofc. you might mitigate some of the downsides but I think the idea of functions is similar to microservices to have a small unite of deployment, well encapsulated.
Finally, you could create a repository per function. It's the most flexible solution but also it might bring some caveats. Ultimately I could imagine every function uses a different version of Spring Boot, some functions are written in Java and some in Kotlin etc. Every function has a slightly different way of testing and running. This all would make maintenance very hard for you in long run. I believe in keeping all functions within one repo with a common set of libraries and configurations would benefit you in terms of cost of maintenance.
Thankfully to Spring Cloud function abstraction, you can use standalone web application by importing the required starter https://docs.spring.io/spring-cloud-function/docs/current/reference/html/spring-cloud-function.html#_standalone_web_applications. This will allow you to trigger your function as HTTP endpoint. Additionally, Spring Cloud function provides Maven plugin which allows you to run the function locally (only GCP) function:run https://docs.spring.io/spring-cloud-function/docs/current/reference/html/spring-cloud-function.html#_getting_started_3

Related

When to use web api instead of dependency injection in a Service Oriented Architecture

In a project I have different processors for each request message that each of these processors can be enabled or disabled based on the configuration. I have two options, one is to use the jar library of each of these processors in my applications and use its classes and the other option is to make each of the processors like a standalone web api that get and return json objects and in this way the communication between these processors would be based on web api instead of using jar libraries.
Which of these options do you think is better and what I need to consider when making such a decision.
Thank you
Depends on what the service is, but overall...
Using the jar will save you network hops between your client and the new service
Conversely, if the jar is taking up a lot of your resources, it might be more performant to have it as a separate service on a different machine
jar file is easily manageable as a project dependency, whereas api service will likely involve a more involved release process
if you manage the jar file, you are probably prone to tighter coupling of code since you are in control of it. having an api somewhat pushes you in direction of writing somewhat cleaner code
I think it really comes down to what your jar is doing and what makes most sense with the service you've packaged in its own jar.

How to share business logic among multiple applications

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

Handling multi-war setup with shared parent spring context in tomcat with maven

This may be a very rudimentary question, but please help me out if this is well-known and has been solved elsewhere.
I have a multi-war setup (all maven modules) say kilo-webapp1 and kilo-webapp2 as two WARs that I need to deploy on a Tomcat instance. These two webapps both use services from a common service jar, say kilo-common-services.jar. The kilo-common-services.jar has its own spring context that is loaded by the users of the jar viz. kilo-webapp1 and kilo-webapp2 in this case. It so happens that the initialization of the services in kilo-common-services takes a long time and hence I want it to happen only once (to ensure that the time it takes to bring up the instance is not very high) which also helps me to use it as a second level cache that it kept current in the JVM instance. To do this, we resorted to the following steps:
Modify the catalina.properties of CATALINA_BASE in tomcat to have shared.loader as ${catalina.base}/shared/lib
Copied the kilo-common-services.jar and all of its dependent jars to the CATALINA_BASE/shared/lib. [Manual step]
Copy spring related jars to the CATALINA_BASE/shared/lib location [Manual step]
Created a beanRefContext.xml file in kilo-common-services.jar. Define a new ClassPathXmlApplicationContext here, where the constructor was provided with the location to the spring context file for the common services.
Noted the dependency scope of kilo-common-services.jar and every other dependency (like Spring related jars) as provided in the kilo-webapp1 and kilo-webapp2 pom files. For Spring this is needed to ensure that the classpath scanning actions are not triggered twice. Also this causes different ClassCastExceptions (for log4j lets's say) if not excluded via the provided scope.
web.xml for kilo-webapp1 and kilo-webapp2 indicated that the parentContext for them is the servicesContext defined in kilo-common-services.jar.
I was able to verify that only one instance of the services of kilo-common-services exist, but the setup as you might have imagined is painful. If someone has best practices about such a setup in an IDE like Eclipse, would really appreciate it. My problems are as below:
#2 is becoming a challenge. I am currently running mvn dependency:copy-dependencies on kilo-common-services to copy dependent jars from target/dependency to the shared/lib which is a woefully manual step. Time and again, I forget to regenerate dependencies and have to do a redeploy again.
#3 is also not straight-forward as time and again there are newer common dependencies and we always have to remember to copy it to shared lib to avoid ClassCastExceptions
#5 is again a maintenance nightmare.
Also as time progresses, there will more such disparate common jars that need to be shared and it would involve pain for each of those jars. Feel free to critique the setup and propose a better one in its place that may be easy to use (from an IDE as well). Would be happy to provide any other details.
Thanks in advance!
The problem is that your architecture is broken (and that's why you're struggling with the solution). You have two solutions:
1) If you want to share a service that takes a long time (to initialise) between two war applications, make that a separate service completely and access it via rest or any kind of remoting.
2) Merge both webapps into one.
Having the common library is the shared lib folder is going to bring you lots of headaches, and you'll end up rolling it back.
My (personal) approach would be to merge both applications, but keep the packages separate enough and have separate spring configurations. In this way, at least you still keep the logic separation of both webapps.
Also since both run on the same container, there's little gain from having 2 separate wars (unless you're planning to move them to different containers very soon).
About the IDE, you can use the maven-cargo-plugin to start up a tomcat with several web applications with (almost) any configuration you want.
We are developing restful soa, with spring and tomcat and utilizing Domain Driven Design (well thats the plan anyway). There is migrationProject and a initial basic search service. Two separate WAR files, with two separate POMs. Both utilize the same Domain objects.
So I will have separate project that will be just the DomainObjects I will wrap them up into a jar, and then using maven and/or jenkins it will deploy automatically (whenever I configure (for example when pushed to a specific repository).
Having two copies of the same jar, sounds like a much worse idea to me. Its not your architecture that is broken, its your deployment and development process thats needs improvement, imho.
(my kind of related question).
Our long term plan is to have one project as the restful interface, with multiple Controllers that have service classes and repositories injected into them from their dependencies.

How can I include a jar file in a distinct package when deploying

I have an ant script that I use to build my J2EE application and create jar files. The problem is the following: Two jar files are necessary for the application to run.
commons-math-2.0.jar
commons-math-1.0.jar
However, I want to only use the 2.0 for a particular package inside the application with the rest of the application using 1.0. How can I build the application to only use the 2.0 version for example with a package name such as com.naurus.eventhandler.risk? Again, I'm using an Ant script, but if there's an easier way to do this sort of thing I'm willing to experiment. Thanks!
If the two jars contain different classes/packages there should be no problem to have all of them in the application classpath. It is then a matter of discipline not to use the classes from the one jar in the other package.
However I guess these two jars contain mostly the same classes/methods? There are many ways of using different versions of the same classes:
Using different ClassLoader instances. I would not qualify it as "easy", far from it means opening the door to a bunch of nasty bugs. (can be helped using a tool like OSGi)
Splitting the application in two processes, these process being launched in the same Ant target and using any mean (CORBA, RMI, REST, etc.) to communicate.
I would not advise using any of these methods though. It would probably be simpler to make all your packages use the same version. Is there any specific difficulty in doing so?
That will be problematic since both JAR files will end up in the same classpath when you deploy your J2EE application. You could achieve what you are trying to attempt with OSGI bundles, which allow each package to have separate dependencies. However, that is a relatively large refactoring of your application.
IMO, it would be best to either:
a) Duplicate the features you need from 2.0 (if the number is small and the license allows it, e.g., package individual classes).
or
b) Spend the time to upgrade the entire application to 2.0
You could use the manisfest in your jar to define the classpath.
http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html
Although honestly it seems a bit convoluted, but it is your requirement.

Eclipse project layout classpath issues

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.

Categories