simar bawa
12 min readOct 26, 2020

Demystifying Design principles to develop clean, modularized, high performance JVM applications

“In some ways, programming is like painting. You start with a blank canvas and certain basic raw materials. You use a combination of science, art, and craft to determine what to do with them.” — Andrew Hunt

Philosophy of clean coding practice and principles is an ocean of jewels. One must be adept to pick the right ones that fits a particular situation which comes with lots and lots of practice. I advocate clean, maintainable and flexible code, and continuously gets better with every line of code I deliver.

The focus of this post is limited to applications developed using Java, Spring-Boot ecosystem. These tools are a given choice for our enterprise microservices needs. Most of the ideas described in this post are better usage of existing framework and language constructs.

At first glance these ideas appear to be small changes, which is brutally true, but when applied all or parts together on Spring, Java based codebase they certainly add significant improvements. I must admit I was also not able to foresee the benefits of investing time in these efforts during the beginning of a recent project, but as we were inching towards the deadline and things were getting complicated, complex, and started testing your ability; these design decisions really helped me in balancing out speed, quality, and efficiency. I had thought of bringing these ideas to a wider audience hoping to benefit others, hence this post.

Problem Statement

Trivial “hello world ” application cannot capture the demands to demonstrate clean, maintainable and flexible code. Therefore, I decided to develop production alike application with focus on its core domain only.

I am going to use my favorite example of Movies management and tracking system (MMTS). MMTS facilitates its users to keep track of movies, actors, directors. MMTS should allow to accept endorsements for a particular movie. Once an endorsement is received, it should be added to the total budget as revenue. System should allow signing contract with actor(s). Each time a contract is signed with an actor, their fees is adjusted from the budget.

Data : MMTS data persistence should be able to persist, adjust and reflect the endorsements and expenditure for a movie in real time.

System Requirements — Functional

We will focus on the following set of functional requirements when designing MMTS:

  1. Any user of system can create a Movie with movie info, actors, directors, ranking, time, initial budget.
  2. Users can add Endorsements for an existing Movie. Every endorsement is treated as revenue to the the movie.
  3. Users can update status of an actor. If an actor sign contract(status is to sign contract), their fees paid will be adjusted from the total budget.
  4. System should be able to reflect the budget left for a movie, and also provide history of each revenue earned and expenditures for auditing

Use Case Diagram

Domain actors — MMTS have user registration, user login management, authentication. But for sake of demonstration, my code will focus only on core domain actors. We identified to have the following actors in core domain of MMTS:

Movie : Represents and manages a particular movie. Assign the actor and director to a movie. Every movie needs to maintain constraints for its budget. Budget is adjustable in movie life cycle.

Actor : Represents one or more actor assigned to the movie. Actors are paid from budget assigned to movie when they sign the contract.

Endorsement : Supports the endorsements and the revenue impact of a particular endorsement for a movie.

Data Model

Budget, movie-info, status, endorsements, revenue, movie, actor, director

It is decided to develop MMTS as a service comprising of Restful APIs per domain actor. The data model and system design is ready, let’s dive into code.

Source Code

The complete source code for application is located here.

Areas of Focus

This post focuses on the following areas demonstrated in above mentioned source code:

  1. Clean user request validations
  2. Decouple exception handling code from business logic
  3. Vertical slicing to develop decomposable monolith
  4. Using Single Table design approach in relational database
  5. Developing event driven components using Spring Application Event
  1. Clean User Request Validation

Dealing with API user request validations is always the most under-estimated but a non-trivial task. This problem become cumbersome once Composite objects comes along; and becomes even more interesting when it requires adding more and more business logic in those validations. It is worthless to point out the frustrations looking at multiple if-else blocks doing those validations. I have seen programmers trying to develop their own frameworks to achieve same task. Just like any other framework it feels pretty in the beginning but tends to gets messy as new requirements shows up especially when designed in a naïve way.

javax.validation comes very handy to handle this scenarios. This is an important language construct, but often least used. Let’s look at the following request specification for creating a movie:

{
"year" : 2020,
"statusId" : 3,
"title" : "There we deal, together!",
"budget" : 20000000,
"info" : {
"directors" : [
"Alice Smithers",
"Bobby Jonas"
],
"release_date" : "06-03-2020T00:00:00Z",
"rating" : 6.2,
"genres" : [
"Comedy",
"Drama"
],
"plot" : "A rock band plays their music at high volumes, during lockdown, annoying neighbors.",
"rank" : 11,
"running_time_secs" : 5215,
"actors" : [
"David Mathewman",
"Anne Thomas",
"Jonathan Nef"
]
}
}

Here are some validation rules for the above user request

1. Movie may or may not have year. If year is present then it 
should be 2000 year to next 2025 years
2. Movie budget is required
3. Info is required
4. ReleaseDate on info is required
5. Directors are required
6. Actors are required
7. Genre is required
8. Ratings cannot be less than Zero
9. Status is required
10. RunningTime cannot be less than One.

Request Object is composed of compositions and leverage javax.validation as shown in CreateMovieRequest, and MovieInfoRequest and this all need is decorating the fields with respective validation rules and few more, as shown below.

@Data
@NoArgsConstructor
public class CreateMovieRequest {
@Min(2000)
@Max(2025)

private Integer year;

@NotBlank(message = "status is required")
private String status;

@NotBlank(message = "title is required")
private String title;

@NotNull(message = "budget is required")
@Digits(integer = 8, fraction = 0)

private Integer budget;

@JsonProperty("info")
@Valid
@NotNull(message = "action items is required")

private MovieInfoRequest info;
}
-------------------------------------------------------------@Data
@NoArgsConstructor
public class MovieInfoRequest {
@NotNull(message = "Minimum one director is required")
private List<String> directors;

@NotNull(message = "Minimum one actors is required")
private List<String> actors;

private String releaseDate;

@NotNull(message = "Minimum one genres is required")
private List<String> genres;

@Min(1)
private Integer runningTime;

private String plot;

@Min(0)
private Double rating;
}

Do not forget to decorate composite class with @Valid, else all validations of composite class will be ignored

Enabling the validation for user request can be done by simply using @Valid along with the object in Spring MVC Controller as shown in Fig 1.1

Fig 1.1 javax validator

This validation executes all the rules, and BindingResult provides a list of all validation failures so clients can be informed of all issues of request. Usually custom validators fails on first failure only, and they generally ignores the rest.

2. Decouple the Exception Handling code from application code

Java is also known for its brilliant Exception handling framework. The purpose of this framework is to handle the exceptions in a very clean manner. However, it is frustrating to work with code where the whole method body is wrapped in a huge try block. Sometimes I wish programmers would had taken a better approach to reduce the exception handling logic by making better use of exceptions instead. I advocate on designing loosely coupled and smaller modules. This design approach approach also requires a good effort to handle and co-ordinate exceptions from each and every modules before conveying their purpose to final result.

Using @ControllerAdvice and @ExceptionHandler together really helps to decouple the application code from exception handling code. Code shown in Fig 2.1 is a MVC RESTful API responsible to update the status of an Actor.

Fig. 2.1 Exception

The 3 tier application logic starting from actorService.updateStatus(..) is free of try catch block.Code is responsible to execute the business logic only and is throwing a specific Exceptions.

There is NO exception handling logic in any of these application code.

This decoupling is made possible by using @ControllerAdvice and @ExceptionHandler as shown in Fig 2.2.

Fig 2.2 describes the Exception Handler and Exception for the code mentioned in Fig 2.1.

Fig. 2.2 Exception Handler

ExceptionHandler is configured to a reply with a specific API response code and message for a particular Exception TYPE.

As the business logic grows, it becomes a matter of adding new types, thrown from business logic, and adding logic to handle that TYPE in ExceptionHandler.

3. Leveraging the power of vertical slicing to organize code in fine grained units

I was introduced to Vertical Slicing in one of the SpringOne Conferences where Oliver Gierke evangelized the idea of developing decoupled monoliths with vertical slicing. The idea is very simple - Organize code of one features as a separate slice. The code base is then comprised of collection of those slices and developed, deployed as a monolith. If any part of this decoupled monolith starts creating scaling problems, then take the slice out into its own service. This helps you start with a good monolith design but flexible enough to move the code quickly into microservices.

This talk came around those exciting times when microservices were becoming mainstream, and monoliths were seen as the worst things happened to mankind. According to some, microservices is the only way to save this planet. Later it was realized that microservices is not that easy, and microservice Architecture is not a silver bullet either.

This talk helped people understand the idea of developing decomposable monoliths. i.e. start with developing fine grained monolith and then use Strangler pattern to decompose it into microservices when needed.

It is not the language that makes programs appear simple. It is the programmer that make the language appear simple. — Robert C. Martin

I initially tried vertical slicing to develop new isolated features in legacy code base(s), and after a certain practice and seeing benefits, I now try to apply this design approach on almost every part of software. Vertical slicing is just a design philosophy and it does not define any structure on its own unlike software design patterns. Also, it heavily depends on one’s interpretation of this principle. That makes it little hard to explain but let me try based on my understanding so far.

Fig. 3.1 code package structure using vertical slices of Actors

Fig 3.1 demonstrates the organization of code per domain actor. As an instance, domain/movie contains all the code required to act on Movie actor. Similarly, domain/actor contains all the code required for Actor. These two slices do not share any domain specific code. If a need arises in future we can easily decompose the slice domain/actor or domain/movie into its own microservices.

An important point here is that we still have to deal with common code which is used across all the actors as in movies/common or movies/config etc. This code contains cross cutting concerns and is shared. This approach requires to spend great deal of time on design upfront, and continuous improvisation on design as you progress. Failing to pay continuous attention, when code base grows and have long running transactions might have side effects. When the code becomes complex, it is often convenient for teams to blur the boundaries of code to make things work. This approach requires a persistent effort and commitment to clean code.

From testability perspective I usually prefer vertical slicing with behavior driven tests(BDD) as oppose to test driven development (TDD) as it feels a natural fit for continuous refactoring of structure. However, good understanding of both of these concepts is required to make sure test boundaries are not blurring and test cases are providing value.

4. Use of Single Table Design in relational data model

When dealing with data like lookups which is often static in nature, we tend to design lookups per entity as in Fig 4.1.

Fig. 4.1 Existing — Many to One Status Lookup approach

With this exiting approach our database tend to have fewer smaller tables and the Ids are served as foreign key wherever those statuses are referenced.

Well this approach had served well but I notice following 3 issues:

  1. This design tend to waste memory in database to hold fewer records in a multiple separate table. When database is hosted in cloud every data, each table, and every query have an associated cost.
  2. Queries requires joins impacting query performance.

3. Status cannot be validated in upper layers of code un-till the lowest layer of database throws Integrity Constraints violations due to an invalid id.

To improve this design, I had decided to tried to adopt Single Table Design approach as shown in Fig 4.2.

Fig. 4.2 Single Table Design to serve all lookups

I had created one single table containing all the statuses for all entities in the application.

Pardon my exaggeration if you feel so — This design approach is inspired from Single Table design concept of key-value family of NoSQL databases like DynamoDB and Riak allowing them to hyper perform at scale.

This approach not only helps reduce unnecessary lookup tables but also improves performance for working with statusId in code. Hosting all data in single table allows to load the data of this table in memory during application bootstrap in a custom data structure, which then serve this data to any module in near constant time directly from memory while reducing the database query. Each time application uses the id for a particular entity, code will look up Type from Map in constant time and then traverse list for that particular Type in linear time proportional to number of record for that particular Type. Please follow this code for more details. There is only one query required to load all lookups.

Fig 4.3 Data Hiding — OOP principle

In-memory data should not be exposed to any part of application directly. Hence, the abstraction is provided as in Fig 4.3. Just a OOPs design principle.

5. Using Spring Application-Events for loosely coupled Event driven modules and Event Sourcing

Let’s have a close look at following requirements

  1. System need to reflect the total available budget for a movie.
  2. When an actor updates the status to sign a contract for movie their fess is adjusted with total available budget , i.e. fess is reduced from total budget.
  3. When an endorsement is signed for a movie, it is considered as revenue for a movie. The client should be able to provide a endorsementId, amount for a movie

4. Business is interested in tracking these changes for auditing.

5. Non-functional requirements : Working on these business events for endorsement or revenue can be extended to other modules, but they should not add latency to existing features.

The requirements dictate use of Event driven approach, precisely Event Sourcing style to mange these transactions for auditing. EndorsementRevenueEvent and ActorFeesEvent are 2 events decided to be used in system for these features.

Fig 5.1 Event driven components using Spring Application Event

Spring ApplicationEvent comes very handy to deal with this scenario. Inherently in an event driven system firing of event, definition of event, and handling of event are decoupled from each other by leveraging an Event Bus of a Queue.

Spring ApplicationEvents really extends this loose coupling principle to application components. There is no need to manage separate queues or buses for events. The programmer is only responsible to define the event, publish the event, and writing logic to handle the event.

The event handler logic can be as complex as the need and it can be transactional or non-transactional.

Fig 5.1 demonstrates the usage of ApplicationEvents for ActorFeesEvent.

Depending on the needs the listener can be Sync or Async which can be easily accomplished by decorating the listener with @Async. Also we can have transaction bound event listener @TransactionalEventListener

Another benefit is that Unit testing the Event publisher is as easy as mocking any other component and verifying the behavior.

Managing events always comes with technical overhead. In Domain driven design Domain Event play a significant role. Using Spring Application Event for handling Events and then providing business logic as delegates really helps to maintain flexible code. Please have a look at the event specific code here and when we execute the code this apply the events and the following chnge in state can be noticed.

The initial movie data have following records as shown in Fig 5.2.

Fig 5.2 Movie data during creation

Let’s operate on this data by applying events by using APIs as explained below explained .

1. POST /actor/status{
“actorId” : 1,
“statusId”: 6,
“amount” : 100000,
“movieId” : 1
}
2. POST /endorsement/add
{
"endorsementId" : 122,
"amount" : 500000,
"movieId" : 1
}
3. POST /endorsement/add
{
"endorsementId" : 123,
"amount" : 500000,
"movieId" : 1
}

Please check the records now in database as in Fig 5.3 after these operations.

Fig 5.3 Event Source style for movie budget

Before closing I want to mention that Persistence and little stubbornness is required to maintain cleanliness of code. It is quite easy to give up when things gets complex. Developers can still make software work by creating big ball of mud, but that’s not how Software Engineers are meant for. They earn their proud and respect by doing things better, always improve and help their business by providing simple solution for complex needs.

Thank you reading so far and spending your valuable time. Please feel free to share feedback should you have any.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

simar bawa
simar bawa

Written by simar bawa

Senior Engineer who owns Architecture, hands-on development, and lead development teams. Currently working for a FinTech company in Silicon Beach.

No responses yet

Write a response