These opinions are there to simplify our development cycle... — Strong patterns guide our code into the right shape. — Lock into the right path, and build great systems. — The core tenets and opinions of Lagom are outlined in “Reactive Microservices Architecture” – a short, free ebook by Lightbend's co-Founder & CTO, Jonas Bonér. JavaDay Kyiv 2016 5
made up of more than one microservice–a system. — Many existing frameworks make individual microservices easy, but systems hard. — Lagom aims to let us build great microservices which compose into great systems. JavaDay Kyiv 2016 7
the beginning, that Lagom provide a great development environment. — It shouldn't take a mountain of scripts to boot dev instances of our system. — A new developer should be able to jump right in, setup without any special scripts, and start coding. — Lagom is just that. Complete with hot reloading of services when code changes. JavaDay Kyiv 2016 9
in a timely manner. — Resilient: They stay responsive in the face of failure. — Elastic: They can remain responsive under varying workloads. — Message Driven: They have a strong message driven foundation... they react to events. JavaDay Kyiv 2016 12
ground up to help you build true Reactive Systems. Lagom is Asynchronous by default, from the I/O layer up. - Akka Streams for async streaming. - Java 8 CompletionStage for async computation. - Interservice communication via streaming. - Websocket support builtin, supporting streaming & async messaging. JavaDay Kyiv 2016 13
with strong, proven technologies to make sure you succeed. — A Java 8+ API* leveraging Lambdas provides a powerful, fluid API for building Lagom services. — Cassandra acts as the default database for Lagom's Persistence module– the development environment even comes with an embedded server. — The popular Jackson library provides a default implementation for JSON Serialization & Deserialization. * A Scala API is forthcoming JavaDay Kyiv 2016 15
Lightbend's Netty based web framework powers the HTTP Engine of Lagom. — Guice dependency injection works hand-in-hand with Play to simplify configuration and composition. JavaDay Kyiv 2016 16
battle tested Actor framework, is the foundation of Lagom services. — Clustering, to easily scale across multiple servers.§ — Persistence, for Event Sourcing. — Streams, for strong asynchronous interactions. — Kafka acts as a message broker for interservice information sharing. Like Cassandra, it is built into the development environment. § Subscribers to the Lightbend Reactive Platform can access additional features with ConductR and the Split Brain Resolver JavaDay Kyiv 2016 17
these are known as Service Descriptors. Service Descriptors define the invocation and implementation, as well as metadata on how to call and serve them. These descriptors are transport agnostic–they are equally usable with REST, Websockets... or any other transport layer. JavaDay Kyiv 2016 19
{ ServiceCall<String, String> sayHello(); default Descriptor descriptor() { return named("hello").withCalls( call(this::sayHello) ); } } — The name of the descriptor - HelloService - is used by services to for discovery. — We have defined a single service call: sayHello — With REST, Lagom maps this to a POST request on the path /sayHello, with a Content-Type: text/plain for both request and response bodies. JavaDay Kyiv 2016 20
pathCall("/order/:id", this::getOrder) ); } It is also possible to accept path parameters; Lagom will attempt to deserialize the appropriate type from the path argument. JavaDay Kyiv 2016 21
— Our ServiceCall is invoked when consuming a service. — Request: type of incoming request message (e.g., String). — Response: type of outgoing response message (e.g., String). — By default, JSON is used as the serialization format for request/ response messages. — There are two kinds of request/response messages–Strict, and Streamed. JavaDay Kyiv 2016 23
objects. — The messages are buffered into memory, and then parsed from the appropriate source, such as JSON. — If both Request and Response are strict, it is considered to be a synchronous call. JavaDay Kyiv 2016 24
{ return named("clock").withCalls( pathCall("/tick/:interval", this::tick) ); } — Streamed messages represent an Akka Stream, and are represented by the Source type; they allow truly asynchronous handling of messages. — In this example, we have a strict request, and a streamed response. In this case, we will stream out a String at the specified interval. — Streamed service calls automatically select a Websocket transport protocol. JavaDay Kyiv 2016 25
{ public ServiceCall<String, String> sayHello() { return name -> completedFuture("Hello " + name); } } — To implement our service, we provide an implementation of the descriptor interface. — Note that this example is implemented using a Lambda, meaning the returned result is not immediately executed; this allows for easier call composition. JavaDay Kyiv 2016 26
- Only a service has direct access to its DB. - Other services must access the DB through the service's protocol. - Since we've already admitted to being opinionated: The best way to do this is Event Sourcing and CQRS... JavaDay Kyiv 2016 28
as events– which are treated as immutable records. — Rather than having advanced Object-Relational mapping, events are captured directly to an event log (AKA a journal) — This event stream can be analyzed to derive useful information, building new read views without complicating writes. — Excellent write performance due to append-only writes. — Easy testing and debugging–simulation is often trivial. JavaDay Kyiv 2016 29
treating write operations distinctly from (most) read operations. Because of this, it pairs nicely with Event Sourcing. — Writes are always targed to a specifically identified entity; we can do reads on specific entities in the same code as well. — Reads that require more complex views, such as looking at multiple entities, are built as separate sections of code. JavaDay Kyiv 2016 30
of value from this pairing, especially given the appending log of events. - Time traveling (playback of events, such as rebuilding state) & auditing become trivial. - Say goodbye to the tyranny of ORMs, and never suffer a database migration script. - Performance, Scalability, Testing, and Debugging all benefit from the event logged model. JavaDay Kyiv 2016 31
Event classes, and subclass PersistentEntity - Define Command and Event handlers. - Entities can be access from anywhere in a cluster. - Conceptually, this corresponds to an Aggregate Root in Domain Driven Design (DDD) JavaDay Kyiv 2016 32
to access it from service implementations (and other places). - Interaction with a PersistentEntity is accomplished by sending it Commands. - Commands are processed sequentially, one at a time (per entity instance). - Depending on our code a Command may result in a state change, which can be persisted as events (which represent the command's effect). - NOTE: The current state is not stored for every change; it can be derived from the replay of the events. - The event log is append only, with no mutation. This yields high throughput and write efficiency. JavaDay Kyiv 2016 33
cluster nodes. — Entities are kept alive, holding their state in memory for as long as it is used. It may eventually timeout and automatically be passivated. JavaDay Kyiv 2016 34
by default in a Lagom project; we will need to add a dependency. SBT: libraryDependencies += lagomJavadslPersistence Maven: <dependency> <groupId>com.lightbend.lagom</groupId> <artifactId>lagom-javadsl-persistence_2.11</artifactId> <version>${lagom.version}</version> </dependency> JavaDay Kyiv 2016 35
whole opinionated thing?) datastore is Cassandra. Being highly scalable and distributed, Cassandra is a perfect fit for Lagom's model. JavaDay Kyiv 2016 36
public class Post1 extends PersistentEntity<BlogCommand, BlogEvent, BlogState> { — Command - the base interface for a command. We implemented it as BlogCommand. — Event - the base interface for an event. We implemented it as BlogEvent — State - the base interface which represents the current state of the entity. We've implemented this as BlogState JavaDay Kyiv 2016 38
implement. It returns a Behavior, representing how our entity handles Commands. The Lagom API provides a mutable builder via newBehaviorBuilder to simplify this. @Override public Behavior initialBehavior(Optional<BlogState> snapshotState) { JavaDay Kyiv 2016 39
BehaviorBuilder), we can register command handlers. b.setCommandHandler(AddPost.class, (AddPost cmd, CommandContext<AddPostDone> ctx) -> { final PostAdded postAdded = PostAdded.builder().content(cmd.getContent()).postId(entityId()).build(); return ctx.thenPersist(postAdded, (PostAdded evt) -> // After persist is done additional side effects can be performed ctx.reply(AddPostDone.of(entityId()))); }); — Command handlers are invoked for incoming messages, and must return events to be persisted (if any) JavaDay Kyiv 2016 40
of a Persist directive. These define which events (if any) to persist to the event log. — thenPersist will persist a single event — thenPersistAll will persist several events atomically (i.e., either all events are stored, or none if an error occurs) — done indicates no events are to be persisted. — It is possible for us to perform side effects after successful persistence, using afterPersist JavaDay Kyiv 2016 41
to validate a command before persisting any state change. ctx.invalidCommand or ctx.commandFailed will reject a command. b.setCommandHandler(AddPost.class, (AddPost cmd, CommandContext<AddPostDone> ctx) -> { if (cmd.getContent().getTitle() == null || cmd.getContent().getTitle().equals("")) { ctx.invalidCommand("Title must be defined"); return ctx.done(); } JavaDay Kyiv 2016 42
not required to effect a state change. A common example of this is a query command, which would instead return the current state to the caller. To do so, we register a setReadOnlyCommandHandler, and invoke the reply method on the context. b.setReadOnlyCommandHandler(GetPost.class, (cmd, ctx) -> ctx.reply(state().getContent().get())); NOTE: Commands must be immutable to avoid the perils of concurrency. We don't want someone mutating our command instance from outside! JavaDay Kyiv 2016 43
successfully persisted, we update the current state by applying the event. To do so, we register a handler with setEventHandler on the BehaviorBuilder b.setEventHandler(PostAdded.class, evt -> state().withContent(Optional.of(evt.getContent()))); — This handler returns the new state; our state must be immutable (that pesky concurrency thing again). — The current state is always accessible from within our event handler via PersistentEntity's state method. — We should define one event handler for each event class our entity persists. JavaDay Kyiv 2016 44
what type of message to send as a reply - the class must implement the PersistentEntity.ReplyType interface. — It is possible to change the behavior of an Entity; useful to implement a Finite State Machine. — Snapshots allow you to "roll up" your events to a single point. JavaDay Kyiv 2016 45
Read side is often tightly integrated with Cassandra. To start with, we will subclass CassandraReadSideProcessor, which consumes any events produced by our PersistentEntity, and updates tables in Cassandra which are optimized for our intended queries. — Retrieving data can then be easily accomplished through Cassandra's Query Language, e.g., SELECT id, title FROM postsummary JavaDay Kyiv 2016 46
be used to produce zip, MSI, RPM, DEB, or Docker files. — Lightbend ConductR[^†] (our container orchestration tool) can simplify & enhance coordination of cluster setups. — In addition to ConductR, the Lightbend Reactive Platform[^†] includes: — Split Brain Resolver (for Akka Clustering) — Lightbend Monitoring [^†] The Reactive Platform requires a subscription, but is free to use during development. JavaDay Kyiv 2016 47
Maven (a more common tool for Java devs than sbt), and Kafka — In the near future, we will add a Scala API — Enhanced support for writing integration tests — Work is being done on support for other cluster orchestration tools — Lagom is open source, and we would love contributions to our Kubernetes integration project! https://github.com/ huntc/kubernetes-lib JavaDay Kyiv 2016 48