Meet Lagom, the latest monolith killer

January 14, 2017 Lagom, Play, Microservices

Microservices are a hot topic! But how do we start developing?
Of course, we want to use "event sourcing" and "command query responsibility segregation." And didn't we sign the reactive manifesto? Whatever we do, we must distribute the load! Hmmm. Which database do we use? Surely not an RDMS using dirty, ugly XA transactions! Should we use NodeJS, Java, Scala, or something else?

Feeling a bit overwhelmed? Probably! The internet is full of articles about microservices, and that's a good thing. However, it all seems a bit overwhelming, and it is not clear which you should use. The "Netflix stack" is impressive, and they have provided a lot of solid frameworks.
Spring Boot became an attractive option due to it's "opinionated" design, which makes it easy to create simple (REST) services.

But all of those things are probably known to you, or you did an admirable job avoiding all of those articles the last few years. ;)
So, why am I writing about this topic?
Because there is a new kid on the block: Lagom!

Lagom ?

Lagom claims to offer a full working microservice architecture based on opinionated API's, features, and defaults. If we inspect which frameworks Lagom uses, we immediately notice that it pushes you towards proven technology that is known to perform well in distributed systems. These are technologies such as AKKA, AKKA Streams, Play, and Cassandra! The patterns they use are also worth mentioning: "command query responsibility segregation," "event sourcing" and, last but not least, reactive programming. Currently, the code is Java-based, but a Scala version is on its way.

A sneak peek

All of this seems very promising. However, it also looks a bit overwhelming. Some questions arise. Is it easy to set up? Are we going to lose a lot of time gluing all of the technologies together? How do we even begin?

Luckily, Lagom provides well-written, concise, and easy-to-follow documentation. By running "activator new my-first-system lagom-java" from the command prompt, a new project gets generated, which contains two sample services. I encourage you to try this at home!

You'll notice that the template includes enough to get you started. There is no need to set up your project structure. You'll find a well-structured Java project, which is, in my opinion, a very solid base to get started. Each service has a "contract" module and an "implementation" module. Within the contract module, the interface of the service resides.

Within the hello-world service, we find a simple RESTful service, created using the famous Play framework. Notice that Lagom introduces a ServiceCall type, which represents a service call for an entity. It's a functional interface that takes a request and returns a response, which is potentially asynchronous.

public interface HelloService extends Service {

  ServiceCall<NotUsed, String> hello(String id);

  ServiceCall<GreetingMessage, Done> useGreeting(String id);

  @Override
  default Descriptor descriptor() {
    return named("helloservice").withCalls(
        pathCall("/api/hello/:id",  this::hello),
        pathCall("/api/hello/:id", this::useGreeting)
      ).withAutoAcl(true);
  }
}

For those among us who do not know what "Play" is about, it's
a "high-velocity web framework for Java and Scala" built on Akka. You can compare it to Spring MVC. The key difference is that Play focuses on stateless, highly-scalable web applications, which is, of course, possible using Spring, though this is not its core feature.

For the persistence side, Lagom wrote some neat code that makes the CQRS and ES part unarguably easy, given that you are using Cassandra as a datastore. Cassandra is an excellent choice when creating scalable distributed applications. However, the fact that there is no support for different databases is a significant limitation. Hopefully, some other databases will get out-of-the-box support in the future. For now, if you want to use a different datastore, you cannot use the persistence framework of Lagom.

However, if you are lucky and using Cassandra, you'll notice how trivial it is to create event-sourced entities! You interact with objects using commands. Those commands are translated to events and, depending on their intention, they persist or are used to retrieve the state. Remarkably easy!

public class HelloWorld extends PersistentEntity<HelloCommand, HelloEvent, WorldState> {

  @Override
  public Behavior initialBehavior(Optional<WorldState> snapshotState) {

    BehaviorBuilder b = newBehaviorBuilder(
        snapshotState.orElse(new WorldState("Hello", LocalDateTime.now().toString())));

    b.setCommandHandler(UseGreetingMessage.class, (cmd, ctx) ->

    ctx.thenPersist(new GreetingMessageChanged(cmd.message),
        evt -> ctx.reply(Done.getInstance())));

    b.setEventHandler(GreetingMessageChanged.class,
        evt -> new WorldState(evt.message, LocalDateTime.now().toString()));

    b.setReadOnlyCommandHandler(Hello.class,
        (cmd, ctx) -> ctx.reply(state().message + ", " + cmd.name + "!"));

    return b.build();
  }

}

Now, let's move our attention to the second sample service, "hellostream." In today's market, real-time systems are often a necessity. Lagom promotes Akka Stream for this. Using the same functional interface as previously discussed, a stream gets created. The only difference is that instead of defining the entities directly, they get wrapped inside the "Source" type of Akka Stream. Even more interesting, we discover a dependency! Surely, this will put you on the edge of your seat!

So, how do we contact the "helloworld" service? Using dependency injection! We see that the interface of the HelloService gets injected. As we investigate, we notice that the "Hellostream-iml" depends on the "helloworld-api" module. Unquestionably, Lagom is smart enough to implement the HelloService interface and contact the "helloworld" service using a form of service discovery.
A purist might frown upon this way of working because the two services share a common codebase; in other words, a hard dependency. However, it makes our life a lot easier, as we don't lose time mapping JSON objects and doing other related boilerplate stuff. It's a tradeoff, but it is one you should consider.

public class HelloStreamImpl implements HelloStream {

  private final HelloService helloService;

  @Inject
  public HelloStreamImpl(HelloService helloService) {
    this.helloService = helloService;
  }

  @Override
  public ServiceCall<Source<String, NotUsed>, Source<String, NotUsed>> stream() {
    return hellos -> completedFuture(
        hellos.mapAsync(8, name -> helloService.hello(name).invoke()));
  }
}

Press here to start

Okay, so the code is pretty straightforward, but how complex is it to run this locally? We want to test things without having to go to the moon and back, navigating between an endless pile of scripts! As it turns out, it's far from rocket science! Executing the "runAll" command from "sbt" kicks in a complete development setup.

➜  hi-lagom> sbt runAll 
[info] Loading project definition from /Users/jeroen/zandbak/hi-lagom/project
[info] Set current project to hi-lagom (in build file:/Users/jeroen/zandbak/hi-lagom/)
[info] Starting Kafka
[info] Starting Cassandra
[info] Cassandra server running at 127.0.0.1:4000
[info] Service locator is running at http://localhost:8000
[info] Service gateway is running at http://localhost:9000
[info] Service helloworld-impl listening for HTTP on 0:0:0:0:0:0:0:0:52685
[info] Service hellostream-impl listening for HTTP on 0:0:0:0:0:0:0:0:52165
[info] (Services started, press enter to stop and go back to the console...)

A quick look at the console output reveals that Cassandra gets started together with Apache Kafka. Furthermore, a "service locator" and a "service gateway" join in. Look at us, doing microservices like pros! Last but not least, the two sample services get deployed. Well, ain't that handy: starting a whole microservice architecture with a single command! All this is made possibly by using the reactive platform of Lightbend.

Conclusion

In conclusion, Lagom is a highly opinionated framework created by Lightbend, specifically to create microservices. It's amazing how they combined all of their knowledge and made something that is so easy to use yet powerful! The easy-to-understand documentation is a big plus. I believe they are providing a decent alternative to the Spring Cloud stack, which makes things interesting! Maybe it will force Spring to start consolidating its Cloud projects instead of trying to support as much as possible without a clear view of what's production ready.

It's true that Lagom limits users to a set of technologies and pushes them toward a certain architecture, but hey, not every company is in the reinventing-the-wheel business.
What's my advice? If you are interested in distributed architecture, microservices, or reactive applications, go ahead and experiment with Lagom!

Update

Good news, Lagom 1.2 introduced JDBC support! Thanks Tim Moore!

Jeroen Bellen
Alken
Dissident blogger!