Spring Bootcamp – The REST of It

In the second edition of the Spring Bootcamp series, we will continue to explore building a web service following REST principles. In the first article we created a few simple GET endpoints, in this article we build out an API that uses the rest of the major HTTP Verbs; POST, PUT, and DELETE.

In this article we will also look at using exceptions to control application flow, why you should use constructors for dependency injection, and also get a better understanding of model view controller application architecture and the benefits of following it.

Where's the rest of it? - Memes

Speaking Proper REST

I touched on REST briefly in my previous article. Let’s continue exploring REST in this article, by covering some of its key concepts.

Nouns and Verbs

Two key concepts within REST are “nouns” and “verbs”. Within REST nouns refer to the resources that a web service has domain over. Examples of this could be orders, accounts, customers, or in the case of the code example for this article,User.

Verbs within the context of REST refer to HTTP Methods. There are nine HTTP Methods in total, but five that actually relate to acting on a noun these are: GET, POST, PUT, PATCH, and DELETE.

GET – Operation for retrieving a resources

POST – Operation for creating a resource.

PUT – Operation for updating a resource.

DELETE – Operation for deleting a resource.

PATCH – Operation for partially updating a resource.

REST Endpoint Semantics

The “nouns” and “verbs” create very specific semantics around how a REST API should look. The “nouns” form the URL of endpoint(s), with the “verb” being the HTTP method. The API for the User service we will be creating will look like this:

GET: /api/v1/Users: Returns all Users

GET: /api/v1/Users/{id}: Retrieve a specific User

POST: /api/v1/Users: Create a new User

PUT: /api/v1/Users/{id}: Update a User

DELETE: /api/v1/Users/{id}: Delete a User

Following a properly RESTful pattern allows for a discoverable API and consistent experience for clients/users who are familiar with REST.

Note: As covered in the previous article the /api/v1 portion of the endpoint are part of general good API practices, not related to REST.

Safe and Idempotent

When creating a REST API it is also to keep in mind the concepts of; safe and idempotent. Safe means a request will not change the state of the resource. Idempotent means running the same request one or more times will provide the same result. Below is a chart laying out how the five HTTP Methods relate to these two concepts:

These are the expected behaviors when using these HTTP Methods, it is important when implementing a service that is following a RESTful API  these expectations are followed. If executing a GET operation leads to a state change for a resource, this will almost certainly result in unexpected behavior for both the owner of the service and the client(s). Similarly a PUT operation that gives different results each time it is executed, will also be problematic.

Safe for the Resource, Not the System

A final key point on this, safe and idempotent relates only to the resource being acted on. State changes can still occur within the service, for example collecting metrics and logging activity about a request. An easy way to conceptualize this is viewing a video on YouTube. YouTube will want to collect metrics about what you are viewing, but you viewing a video shouldn’t change the contents (i.e. the state) of the video itself.

Writing Proper REST

With understanding some of the key REST concepts a bit better, let see what they look like in practice. Above we covered five of the HTTP methods, but, as mentioned in the intro, we will be implementing only four of them; GET, POST, PUT, DELETE, as they map closely to the Create, Read, Update, Delete (CRUD) concepts, which will be covered in more detail in a future article on persisting to a database.

In the first article we used @GetMapping to create GET endpoints. Spring similarly offers @PostMapping, @PutMapping, and @DeleteMapping, for creating the related types of endpoints. Below is the code for a UserController which defines endpoints for retrieving all users, findAll(), looking up a specific user by id findUser(), creating a new user createUser(), update an existing user updateUser(), and deleting a user deleteUser():


@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private UserService service;
public UserController(UserService service) {
this.service = service;
}
@GetMapping
public List<User> findAll() {
return service.findAll();
}
@GetMapping("/{userId}")
public User findUser(@PathVariable long userId) {
return service.findUser(userId);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = service.createUser(user);
return ResponseEntity.created(URI.create(String.format("/api/v1/users/%d", createdUser.getId())))
.body(createdUser);
}
@PutMapping("/{userId}")
public User updateUser(@PathVariable long userId, @RequestBody User user) {
return service.updateUser(userId, user);
}
@DeleteMapping("/{userId}")
public ResponseEntity<Void> deleteUser(@PathVariable long userId) {
service.deleteUser(userId);
return ResponseEntity.ok().build();
}
@ExceptionHandler(ClientException.class)
public ResponseEntity<String> clientError(ClientException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<String> resourceNotFound(NotFoundException e) {
return ResponseEntity.notFound().build();
}
}

Behind the controller is the UserService for handling the actual business logic, limited as it is, for the web service. In this example for the “persistence” I am simply using an ArrayList. Note the usage of exceptions in the service class which I will touch on in more detail.


@Service
public class UserService {
private List<User> users = new ArrayList<>();
private static final Random ID_GENERATOR = new Random();
public User findUser(long userId) {
for(User user : users) {
if(user.getId().equals(Long.valueOf(userId))) {
return user;
}
}
throw new NotFoundException();
//throw new ClientException(String.format("User id: %d not found!", user.getId()));
}
public User createUser(User user) {
user.setId(ID_GENERATOR.nextLong());
users.add(user);
return user;
}
public User updateUser(long userId, User user) {
user.setId(userId);
// User equals looks only at the id field which is why this works despite
// looking weird
if (users.contains(user)) {
users.remove(user);
users.add(user);
return user;
}
throw new ClientException(String.format("User id: %d not found!", user.getId()));
}
public void deleteUser(long userId) {
Optional<User> foundUser = users.stream().filter(u -> u.getId() == userId).findFirst();
if (foundUser.isPresent()) {
users.remove(foundUser.get());
return;
}
throw new ClientException(String.format("User id: %d not found!", userId));
}
public List<User> findAll() {
return users;
}
}

The code is available on my GitHub repo and you can run it locally to see how it works.

Understanding the Benefits of Model, View, Controller and Separation of Concerns

Model, View, Controller (MVC) is a popular architecture to follow when building a web service, or at least it is in theory. I’ve seen and have built web services where the line between the model, view, and controller has become decidedly blurred. Let’s review the MVC architectural pattern, where developers often go wrong when implementing MVC, and why it matters to follow MVC architecture when building a web service.

MVC Explained

MVC is an architectural pattern of separating a project based on three distinct concerns;

Controller – This is the interface the user/client interacts with to use the service. In the above code this would be represented by the UserController class.

Model – The model is the real “meat” of a service. This is where any business processing, persistence, etc. occurs. This is represented by the UserService class.

View –  The view what is the users sees. When building a REST API this is largely handled invisibly by Spring; which by default converts returned messages to JSON.

The wikipedia article on MVC provides a visualization of the above:

256px-mvc-process.svg_

Why Good (MVC) Architecture Matters

As the lines between MVC start to blur it can become difficult for a developer to know where to implement new requirements, which sometimes can lead to requirements being accidentally, or even intentionally, implemented in multiple areas. As these issues build up, it can become increasingly difficult to test and maintain an application.

While the User Service we built in this article is very simple, the UserController does represent the level of concern that a controller should contain even in a more complex service. The controller should primarily be concerned with passing values to a service layer and interpreting the return from the service layer to represent back to the user. Inspecting and manipulating the values in a request is a smell that you might be deviating from MVC in a meaningful way.

We will be exploring automated testing in the next article were we will understand the practical benefits of following good architectural practices.

Exceptional Control

Early in my career I was often strongly advised against using exceptions for control flow. Exceptions should be reserved for exceptional conditions; unexpected nulls, failure to connect to a downstream service, incorrect value types, etc.. Errors relating to business reasons should be handled with normal application flows, if/else statements, setting a flag value, and so on.

There are reasons to be cautious when using exceptions to handle application flow, generating a stacktrace, which happens when throwing an exception, is expensive. However using exceptions for control flow can also make code architecturally cleaner.

In UserService, instead of setting a hasError field in User I am throwing an exception when a validation check fails, in this case when a client sends a user id that doesn’t match any existing users. I then make use of Spring’s @ExceptionHandler functionality to generate an appropriate response for the user. As seen in UserController multiple methods can be annotated with @ExceptionHandler each handling a different exception. This allows for a clean way of handling different error responses:


@ExceptionHandler(ClientException.class)
public ResponseEntity<String> clientError(ClientException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<String> resourceNotFound(NotFoundException e) {
return ResponseEntity.notFound().build();
}

To Return 404 or 400 When a Resource Doesn’t Exist?

In UserService I implement two ways of handling what is the same problem, a client sending an id for a user that doesn’t exist. Going by proper REST guidelines a 404 should be returned. The potential issue is that a 404 could be ambiguous, was a 404 returned because the desired resource doesn’t exist or because the wrong endpoint is being used?

As mentioned, by REST guidelines the correct choice is clear, 404, but it may not be the correct answer in every use case. The important thing would be to document clearly the expected behavior when looking up a non-existent resource and being consistent across your service(s).

Constructor vs Field Dependency Injection

For a long time many Spring developers had a habit of using field dependency injection. If you were to go into many older Spring projects, including many I wrote myself, you’d see classes that looked something like this:

“`java
public class ClassA{

@Autowired
private ServiceA serviceA;

@Autowired
private ServiceB serviceB;

//the rest of the class

}<span style="color: var(–color-text);">“`

In the above code snippet above, the members serviceA and serviceB are being supplied via field dependency injection. Configuring dependency injection this way is problematic for two major reasons:

  1. It makes testing more difficult – In order to test the above class you must instantiate the Spring application context, which will slow down test execution and generally increases test complexity.
  2. It can make it difficult to know a class’ dependencies – Injecting via the constructors creates a kind of contract defining a classes dependencies. Field injection does create such a requirement which can lead to tests breaking in confusing ways or code breaking in difficult to understand reasons when a new field requiring dependency injection is added.

A common critique of Spring is that it’s too “magic”, a lot of this magic related back to a reliance on field injection in Spring’s earlier days. To address this, along with updating documentation and code examples to encourage constructor dependency injection, in Spring Framework 4.3 (Spring Boot 1.5) if a class only has a single constructor, Spring will automatically use that constructor for dependency injection. This removes the need to annotate that constructor with @Autowired. This is the Spring team subtly indicating the preferred way of handling dependency injection.

Conclusion

In the first two articles of this series we learned some good practices for building a RESTful API using Spring Boot. In the next article we will take our first steps into the world of automated testing, one of my favorite subjects!

The code in this article is available on my GitHub repo.

Spring Bootcamp – GETting Started

I was recently listening to the Arrested DevOps podcast, in the episode on Making DevOps Beginner Friendly guest Laura Santamaria talked about the importance of creating learning paths. A learning path, as the name suggests, is a series of articles or guides that walk someone through how to use a technology or practice. Learning paths differ from normal blog articles, like I have often done, which cover how to accomplish a very specific goal in isolation.

In the decade I have been working with the Spring Framework in general and the 5 years I have specifically worked with Spring Boot I have learned a lot, what to do, what not to do, and in some cases the why behind some of those answers. With many people working from home in response to the COVID-19 outbreak, seems an opportune time to go back to the basics.

In this series we will do a slow burn through Spring Boot, each article will be structured around the steps to do a complete a common task, but will take the time to explain what exactly the code is doing, what is happening in the background, as well as some of the why/best practices behind the tasks. The goal isn’t necessarily to break new ground in what Spring Boot can do, but to try to get a more well-rounded understanding of Spring Boot.

In this first article of the we will initializing a new Spring Boot project and create a couple of simple HTTP GET endpoints. So with that…

HOMAGE on Twitter:

Initializing a Spring Boot Project

Screen Shot 2020-03-27 at 9.05.06 AM

When starting a new Spring Boot project, one of the best places to go is start.spring.io. start.spring.io provides an interface for defining a project’s metadata as well as the ability to easily bring in many commonly used dependencies that should all be compatible to work with one another. Below demonstrates how to quickly initialize a project:

Note: If you are following along with this article you should bring in the spring-web and spring-boot-devtools dependencies.

Building Web APIs with Spring Boot

For many Java developers, a big part of their day is spent building and maintaining applications that service a Web API. Spring Boot makes building and maintaining really easy, which is a big reason why it has become so popular in the Java world.

After importing a new Spring Boot application into your preferred IDE, we can have an accessible endpoint with just these few lines of code:


@RestController
@RequestMapping("/api/v1/hello")
public class HelloSpringController {
@GetMapping
public String helloWorld() {
return "Hello World";
}
}

Once added, starting the Spring Boot application should result in “Hello World!” being printed when you go to: http://localhost:8080/api/v1/hello.

Let’s look at the key elements from the above:

@RestController: This annotation marks to Spring that this class is a web controller, a class that serves as the interface to the Web for interacting with the internal application.

@RequestMapping("/api/v1/hello"): This annotation allows a developer to define the base path for the entire controller. All endpoints defined in this controller will be pre-fixed with /api/v1/hello.

@GetMapping: This annotation defines that the method helloWorld can be accessed as a HTTP GET.

With “Hello World” working, the second task when working with a new language or framework is to take in some user input to create a message. With a GET endpoint there are three ways of accepting input from a client; via the URL path, as query parameters, and as a request headers. Let’s look at how to reference values from each below.

Retrieve Values from the URL Path


@GetMapping("/{message}")
public String helloMessage(@PathVariable String message) {
return String.format("Hello, %s!", message);
}

To retrieve values from the URL path, in the @GetMapping you will need to define a variable in enclosing braces like above with {message}. In the arguments of the method an argument must be annotated with @PathVariable, if the name of the argument is the same as the variable in the definition of @GetMapping then Spring will automatically map it. @PathVariable has three fields:

name: Allows for manually mapping a url path variable a method argument.

required: Boolean for if the path value is required. Defaults to true.

value: alias for name.

Retrieve Values from the URL Query


@GetMapping("/name")
public String helloQueryMessage(@RequestParam String firstName, @RequestParam String lastName) {
return String.format("Hello %s %s!", firstName, lastName);
}

Values can easily be retrieved from the query portion of an URL, the section of the URL after the “?” e.g.: ?firstName=Billy&lastName=Korando. Spring by default will attempt map query variables to the names of arguments in the method. So in the example URL query firstName and lastName will automatically map to the arguments firstName and lastName. @RequestHeader has four fields:

name: Allows for manually mapping a query value to a method argument.

required: Boolean for if the path value is required. Defaults to true. A HTTP 400 is thrown if a required value is not provided.

defaultValue: A default value for when the parameter is not provided. Will set required to false.

value: alias for name.

Retrieve Values from the Request Header


@GetMapping("/header")
public String welcomeUser(@RequestHeader String user) {
return String.format("Welcome %s!", user);
}

Retrieving values from a request header works very similarly to retrieving them from the URL query. Like with @RequestParam, @RequestHeader will automatically map the method argument name to the name of a header value. @RequestHeader has four fields:

name: Allows for manually mapping a header value to a method argument.

required: Boolean for if the path value is required. Defaults to true. A HTTP 400 is thrown if a required value is not provided.

defaultValue: A default value for when the parameter is not provided. Will set required to false.

value: alias for name.

String.format() or String Concatenate

Commonly when building a String in Java many Java developers build a String using concatenation like this:

"A message with a variable: " + var1 + " and another variable: " + var2 + " and more...";

Constructing a String this way can become difficult to read, and also be a formatting nightmare as the code is constantly changed because of slightly different formatting rules. When building a String it can be useful to consider using String.format() instead as demonstrated above. Readability can be a bit easier and there a number of pre-defined ways for printing things like dates available. For more information on how to use String.format() check out the official Javadoc: 8, 11, 14

Convention over Configuration

I am a longtime Spring user, my first experience with Spring was in 2010, using then Spring 2.5. While Spring was a significant improvement over frameworks I had used prior, initializing a new Spring project was still a difficult and and time consuming process process. Getting a static endpoint running as we have done in this article could take hours, even days, if starting truly from scratch.

We were able to accomplish in minutes with Spring Boot, what took hors before because Spring Boot uses a pattern called convention over configuration. In short Spring Boot has a number of default opinions, such as using an embedded Apache Tomcat server running on port ​8080. Many of of these opinions however can easily be change. If we needed to run our Spring Boot application on a different port, we can just set server.port. Using a different application server can be as easy as making a couple small changes to our build file.

Convention over configuration allows developers to focus on key business concerns, because in many cases using embedded Apache Tomcat and running on port 8080 is enough, especially in an increasingly containerized world. Understanding Spring Boot’s default opinions and how to change them will be a key element through out this series because there are definitely right and wrong ways of changing them.

Restart Revolution

As an application is being built there is often a need to rapidly iterate. This means rebuilding and restarting an application frequently. While the steps to rebuild and restart an application aren’t difficult, performing them can disrupt your “flow”. To address this the Spring team developed Spring Boot devtools. Spring Boot devtools provide two key features; automated start and, with browser extensions, live reload. Here is Spring Boot devtools in action:

To use Spring Boot devtools in your project, you will need to add it as a dependency in your build file like this:


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>

Be sure to check out the user guides for more information on how to use Spring Boot Devtools including how to include exclude additional files, use it within a production system, using it on a remote system, and more.

Proper REST and API Best Practices

Like the code in a project itself, the usability and longterm maintainability of an API depends significantly on how well it is designed. Let’s review a few ways to improve the design of an API.

Version Your API

Probably the first, and also on of the easiest ways, to improve the design an API is to include in the URL the API version. In the examples about this was done with v1. Versioning an API allows it to more easily evolve over time as business and client need changes. When breaking changes are introduced, they can be included a new version of the API e.g. v2 and this much easier for clients to migrate to than forcing a hard and complicated switch if the same API endpoints are used.

Follow REST When Practical

Representational State Transfer, or REST, has become a popular architecture to follow when designing Web based APIs. REST was built upon the HTTP protocol, and while there are legitimate critiques that it might not always work well in every business case, there are a few good elements to follow such as; using the appropriate HTTP verb for the behavior of a endpoint e.g., for retrieving data a GET should be used, creating new resources should be a POSTDELETE for when an resource should be deleted.

Additionally proper usage of HTTP codes can be helpful as well; a HTTP 200 should be returned only when a request is successful. 400 should be returned, along with an appropriate message, when the client sends invalid or bad data. A 404 is also appropriate to return when a client requests a non-existent resource.

Fully following all of REST might not be possible or practical in all use cases, but following some of the key elements above can greatly improve the usability and maintainability of a API.

Conclusion

Spring Boot has been a revelation for the Spring developer community. Spring Boot has allowed developers to quickly build new applications while focusing on designing business valuable features for their organizations. As touched on in this article, there is also a lot of subtly to using Spring Boot.  Spring Boot is easy to get started with, but can take a lot to “master”, as even after five years I am still learning new things all the time. In this series we will continue to explore how to use Spring Boot to its full potential.

The code examples used in this article can be found in my GitHub.

How to Better Captain of the Enterprise with Apache Maven

Apache Maven is popular tool for project management and defining how a project should be built. It’s flexible, extendable, and, because of it’s aforementioned popularity, most Java developers have at least some experience with it, creating a virtuous cycle of its usage.

When an enterprise adopts Maven, a frequent goal is to use it’s features and mechanisms as a way to encourage, or even enforce, enterprise standards and to make project initialization and maintenance easier. Over my career I have worked at a number of organizations, both as a developer consuming POMs defined by architects and technical leads, and as an architect defining POMs that would be consumed across an enterprise.

From this experience I have learned some of the pain points and common mistakes enterprises make when trying to use Maven. Often these pain points and mistakes are borne from a lack of knowledge of Maven’s feature set and/or incorrect usage of features. In this article we will step through how to make Maven more flexible and easier to manage in an enterprise setting.

picard

Being a Better Parent (POM)

One of the most common ways an enterprise utilizes Maven is through the creation of an enterprise wide parent, or super, POM. Parent POMs can be a great tool for encouraging developers to follow enterprise standards and reducing bloat in child POMs be defining commonly used dependencies, plugins, reporting etc.. Parent POMs can also become a huge burden slowing down build times, bloating the size of artifacts, and causing other problems in projects that consume the parent.

Maven provides features though when defining a parent POMs that allow it to be more flexible; offering help when and where it’s needed, but also quietly stepping back when a project might have unique needs and requirements. Let’s take a look at some of these key features.

Dependency Management

When defining a parent POM a common mistake is to add a lot dependencies that are believed to be universally required, but simply are not. Downstream this can cause problems because while it was thought every project would need dependencies X, Y, and Z, in reality some projects don’t need dependency Y, and for other projects; dependency Z causes an error when present on classpath.

Maven does provide a feature that gives a nice compromise between offering assistance with which dependencies should be used, particularly which version of a dependency should be used, and also keeping child parent POMs “clean”, and that is with Dependency Management. Dependency management is easy to use, just define dependencies as normal, but enclose them dependencyManagement tags. Here is an example below of using dependency management:


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.my.developer</groupId>
<artifactId>my-org-starter-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

In the child pom we need only provide the groupId and artifactId of a dependency and all the additional information: version, type, scope, is pulled down transitively.

Screen Shot 2020-01-07 at 12.34.49 PM

Dependency management strikes a nice balance for both downstream developers and the architects and technical leads who are responsible for parent POMs.

Downstream developers benefit by not having to worry as much about which version of a dependency to use, what it’s scope should be, or their projects becoming bloated because a parent POM is adding extra dependencies they do not need. Downstream developers also benefit as the POMs for their projects are “cleaner” as only the artifactId and groupId for a dependency needs to be defined.

Tech leads and architects benefit by still having a centralized location from which to manage dependencies for downstream projects, but not have to deal with the issues that come from imposing the usage of dependencies, had the dependencies been defined normally within a parent POM.

Plugin Management

The same problems with dependencies, can also happen with plugins, and for that Maven offers a similar solution with Plugin Management. When you have a frequently, but not universally, used plugin, defining the plugin with plugin management is a good option.

A common example of this would be the maven-failsafe-plugin. Every project should be using the maven-surefire-plugin to run unit tests, however not every project will need the failsafe plugin which is commonly used for executing integration tests (an example here would be a shared library which might not need to communicate with any remote systems).

By defining the maven failsafe plugin using plugin management, when a project needs to execute integration tests, they need only add groupId and artifactId for the failsafe plugin in their POM, and then all the configuration defined within the pluginManagement of the parent POM. Here is an example of using pluginManagement in a parent POM:


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<includes>
<include>**/ITest*.java</include>
<include>**/*ITest.java</include>
<include>**/*ITests.java</include>
<include>**/*ITestCase.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

And then a child POM pulling down the configuration of the failsafe plugin:

Screen Shot 2020-01-07 at 12.35.21 PM

Properties

Maven POMs also have section for properties. Like with any feature in Maven, and most programming languages, the most local definition of a property overrides the value that’s pulled down from a parent. Importantly though this overriding also applies to how that property is used within a parent POM. In the below example I have defined the property my-org.version, in my parent POM and set it to 0.0.1-SNAPSHOT. Under my dependency management section I reference that property in the version field of ​my-org-starter-security. This means by default the version of ​my-org-starter-security will be set to 0.0.1-SNAPSHOT in any child POM. Here is the code snippet below:


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<properties>
<my-org.version>0.0.1-SNAPSHOT</my-org.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.my.developer</groupId>
<artifactId>my-org-starter-security</artifactId>
<version>${my-org.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

view raw

properties.xml

hosted with ❤ by GitHub

However in a child POMs if the property of my-org.version is defined and set to 0.0.2-SNAPSHOT, then that project will use version 0.0.2-SNAPSHOT of my-org-starter-security even though the version field is not of that dependency is not defined.Screen Shot 2020-01-07 at 1.36.52 PM.png

This behavior would apply to anywhere where properties are used, not just to dependencies. Using properties can allow an easy way for downstream projects to change the version of dependencies they are using, as seen in the above example, or change the behavior of a plugin or reporting tool. As you are defining dependencies, plugins, etc. in parent POMs, strongly consider using properties instead of hardcoded values for fields downstream projects might want to change.

Looser Coupling with BOMs

For many enterprises parent POMs alone will be enough for handling dependency and project management concerns. However for larger enterprises, or an enterprise that works in a number of different domains, they might struggle using parent POMs alone. I experienced this at an organization where we created a whole tree of parent POMs that looked something like this chart:

Screen Shot 2020-01-08 at 2.14.09 PM

There were two major problems here:

  1. The tree was so top heavy that a change in the top POM kicked off a lot of downstream builds
  2. The development cycles between the organization wide parent POM and downstream POMs were not in-sync. So this created a lot of confusion in versioning. If the “Super Parent” had a major version change, should that require a major version change in downstream POMs? If a downstream POM had a lot of changes should it also increase it’s major version or should that only be tied to upstream changes?

This confusions led to  frustration for both the architecture team that I was a part of and the downstream developers as they were constantly finding issues and having to update parent POM versions.

In situations like this, it might be advantageous to use BOM’s, or Bill of Materials, so there is a looser relationship to an organization’s parent POM. A real world example of this is in the Spring framework, the Spring Cloud team publishes a BOM for managing the libraries that are associated with that project as they have a different release cadence from the Spring Boot and Spring Framework projects. You can view the BOM the Spring Cloud team produces here.

Going back the earlier example, instead of a complex tree of POMs and child POMs, a single “super parent” POM could be used and a pair of BOMs that cover the Web and Batch domains could be created. This approach likely would had been less work for the architect team that I was on, and a more pleasant experience for downstream developers as there would be fewer changes to consume.

Screen Shot 2020-01-08 at 3.25.01 PM

Shared Library POMs

In a recent article I stepped through how to create a custom Spring Boot starter. When creating a library that will be shared within your enterprise it’s important to take care when defining the library’s POM. A developer’s perception of a shared library can sour quickly if using it brings in extra dependencies, causes classpath conflicts, or leads to other problems. Let’s look at some strategies for when creating a shared library’s POM.

Scopes

Maven has several scopes that can be used to define when and under which circumstances a dependency should be included. The scopes are; compile, provided, runtime, and test.

  • Compile: is the default scope and means that a dependency must be available when a project is being built and packages in its artifact.
  • Provided: the dependency must be present when the project is being built, but will be provided by the system that will be executing the artifact.
  • Runtime: the dependency isn’t required to build the artifact but must be present when running the artifact.
  • Test: the dependency is only used as part of the test cycle, and not required at runtime.

Using these scopes correctly can help avoid unnecessary dependencies being brought and give developers consuming a library more control on what dependencies are being brought in.

Optionals

Some dependencies might be required only in certain circumstances. In my Spring Boot starter example,  my-org-starter-security, the starter could be used in a web context and a console context. To build the project the spring-boot-starter-web and spring-security-web dependencies needed to present, but only needed if the project consuming my-org-starter-security is a web project. By setting spring-boot-starter-web and spring-security-web as optional console developers don’t have to worry about a bunch of extra web libraries being brought in. Below is an example of setting some dependencies as optional in a POM file:


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.framework.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.framework.version}</version>
<scope>compile</scope>
</dependency>
<!– Optional Dependencies –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.framework.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

Examples at Scale

Small scale demonstrations on how to use the features highlighted in this article is one thing, but often new problems occur at scale or once business requirements are introduced. To see examples of these concepts applied at scale I would recommend checking out how the Spring team has structured their POMs which can be found on their project GitHub repos:

https://github.com/spring-projects/spring-boot

https://github.com/spring-cloud

Conclusion

Image of star trek next generation enterprise flying in space

Proper management of parent POMs and shared library POMs can make a world of difference in how smoothly an enterprise operates. Define them well and you have happy developers and architects who can explore more important questions. Define them poorly and you will find yourself stranded while you try desperately to fix problems.

This article focused on a few key features to make managing maven easier in most enterprise settings, it only scratches the surface of all that Maven can do however. If you want to learn more be sure to check out the official documentation, as well as this great article which steps through a full featured maven pom.

You can view the code for this article on my GitHub Repo.

Three Books Every Developer Should Read

A lot goes into growing your career and knowledge as a developer. There are many ways to learn; the most common would be experience from the day-to-day work of being a developer, a bit more self directed would be; building small proof of concepts with a cool new tool or framework, reading blog articles, watching videos, or listening to podcasts, all are great ways to learn, and each provide their own unique benefits. However these forms typically are more concerned with answering the what or how. Books are another popular form of learning and where they differ from the other forms is their focus on the why.

Books, by focusing on the why, help to explain the underlying principles and rationale behind a practice or pattern. It is one thing, for example, to know that it is a good practice to wrap all calls to an outside service in a circuit breaker. It’s another to understand the goals and benefits behind the practice. This deeper level of understanding can help a developer make the move from junior to senior in more than title.

In this article I’m going to offer recommendations on three books that have read that have really helped me in my career. These books cover a wide range of subject areas; system/application design, reliability, and delivery. While the code examples in the book may be written in a language different from what you are familiar with, the principles are technology agnostic. So rather you are a; C#, Java, JavaScript, frontend or backend developer all these books should still be relevant and beneficial.

Domain Driven Design

Image result for domain driven design

Author: Eric Evans, Amazon

If you have wondered where the term “Domain Driven Design” came from, it is from this 2003 book. The concept of Domain Driven Design (DDD) has been talked about, blogged about, and tweeted about, a lot since its initial release. The quality of the commentary can vary, but it’s difficult to properly condense down a 560 page book into a 1000-2000 word blog article or hour long talk.

Despite it being written all the way back in 2003, its lessons are still as relevant as ever. A concept particularly important for today would be bounded contexts. If you are considering switching to a microservice-style architecture, finding the edges of a context is critical or else you might end up changing very cheap method calls inside a monolith application with very expensive http calls between services.

Favorite Lesson in the Book

Ubiquitous Language –  Early in my career I would frequently see code that would have very generic names Processor, a, validate, and so on. Figuring out what the code was doing or how it related to business needs was difficult. Ubiquitous Language describes writing code using the same nouns and verbs that your team uses to describe business concepts. So instead of Processor you might have CuustomAccountProcessor, appointment instead of a, and validateMailingAddress.  While the concept of “self-documenting code” is a myth, writing code in this way helps to lower the learning curve for new developers, or even for yourself months later when returning to a project or module.

Release It!

Image result for Release IT

Author: Michael Nygard, Amazon

Michael Nygard’s 2007 book covers how to design applications and systems to be fault tolerant and provide a good experience to clients when problems inevitably occur. The book covers several areas that can affect the performance of an application and/or system. The book is written in an anti-pattern/good pattern format, first covering a widely followed anti-pattern, the problems from following that anti-pattern, and then a good pattern that addresses the same need, but creates an application or system that is more stable, responsive, and able to recover from outages.

The image above is from the first edition, which as mentioned was released in 2007. This is the edition of Release It! I have read, but a second edition was released early last year. The Amazon link above is to the edition of the book which is the edition I would recommend reading. The reviews of the second edition suggest it is just as well written as the first edition.

Favorite Lesson in the Book

Limiting Cascading Failures – No matter the careful planning, amount of resources available, or good design practices followed, failures will happen. Finding out the cause of a failure is something to worry about later, what is important, in the moment, is returning to normal state as quickly as possible. The steps to getting to normal state will vary, but it will always be faster and easier if you design systems to limit the scope of a failure. If a database goes down, it will always be harder to return to normal state, if the application(s) that depend upon that database also crash and/or need to be restarted.

Several organizations I have been with didn’t see as much of an issue if a downstream service also crashed/needed to be restarted, the thought being they wouldn’t be usable anyways. This always complicated the process of returning to a normal state and additionally impacted deployments as there was a deployment order requirement. Designing systems to limit cascading failures makes recovery from failures faster and can also have the added benefit of making deployments easier as well.

Continuous Delivery

Image result for continuous delivery book

Authors: Jez Humble and David FarleyAmazon

On the subjects of deployments we have, Continuous Delivery. Not that I am playing favorites with my book recommendations, but Continuous Delivery has had the most profound impact on my career and how I view software development. The lessons in Continuous Delivery really resonated with me as it often covered the pain points I was often experiencing as a developer at the time and offered much more practical and sensible solutions that addressed them.

Continuous Delivery is one of the major reasons for why I got really interested in automated testing, as automated testing is the foundation on which continuous delivery (the practice) is built. Generally speaking my opinion is that if more organizations got continuous delivery right, it would address a lot of the other problems they are frequently dealing with.

Favorite Lesson in the Book:

Auditability and reproducibility – The major theme in Continuous Delivery is automating the delivery pipeline to production. When the subject of automation is discussed, often its benefits are described as being increased speed and reduced costs from replacing slow and expensive manual labor with processes executed by machine (script). These are certainly significant benefits, but, while subtle, the biggest benefits of automation in my opinion are its quality of being auditable and reproducible, which are also covered in Continuous Delivery.

Automated processes by their very nature are auditable and reproducible. Want to know what an automated test is doing? Look at the code for the automated test. Want to see what happened during a deployment? Look at the logging from the system the executed the deployment script. Automated tests can be repeated, deployment scripts reran to narrow in on and investigate a potential issue. Not to overhype automation too much, as it’s not a panacea, but its benefits over manual processes are difficult to overstate.

Conclusion

There are many great books on software development available. This is only a small selection that I have found particularly helpful. I have found myself frequently thinking back to lessons learned from these books and referencing these books in presentations and blog articles. Taking the time to read some books is really important for all developers. Rather it’s the books listed in this article or others, I hope you set aside some time to pop open a book. While benefits may not always be immediate and often require a relatively high investment in time, books can provide longterm returns in ways other types of learning may not always be able.