Flange™

“Managing the perforations.”™
Lighweight ecosystem of loose coupling libraries.
https://flange.dev/

Copyright © 2023 GlobalMentor, Inc.
https://www.globalmentor.com/

Flange DI

Choose your own dependency injection:
  • Spring
  • Guice (upcoming)
  • Picocontainer (upcoming)
  • Csardin (Flange's own tiny DI container; upcoming)

Flange Cloud™

Transparently transforms and deploys a well-designed monolith application to a distributed, elastic, highly-scalable cloud-native application.

The Application

“Once upon a time there was an application …”
A Monolith Mess

It was obviously a …

mess

monolith.

😱

“The application has gotten too big and too hard to manage.”

“How can we change the application to make development easier?”

The Revamped Application

A Microservices Mess

It became a …

mess

microservices architecture.

🎉

Microservices Architecture

(as typically described using containerization)
  • 🎉 Services can be independently developed.
  • 🎉 Services independently scalable.
  • 🎉 Services independently deployable.
  • 😐 Services can be written in Python/Node.js.
  • 😐 Services can communicate using RESTful APIs.
  • 😱 Deal with service discovery.
  • 😱 Sidecars, load balancers, auto-scaling rules ….
  • 😱 Manage a Kubernetes cluster.

“When Should You Use a Monolith?”

If you anticipate the size of your development team to stay under 15 people for the next 5 years, and expect to have less than 10 million active users, you may want to keep things easy by sticking with the monolith.
(emphasis added)
Jason Katzer, Learning Serverless (O'Reilly, 2020)

“When Might Microservices Be a Bad Idea?”

  • Unclear Domain
  • Startups
  • Customer-Installed and Managed Software
  • Not Having a Good Reason!
Doing microservices just because everyone else is doing it is a terrible idea.
Sam Newman, Monolith to Microservices (O'Reilly, 2020)

Case Study: Istio

… although the Istio team was aware of Sam Newman’s microservice recommendations, they didn’t give appropriate weight to the guidance ….
The Monolith Strikes Back: Why Istio Migrated From Microservices to a Monolithic Architecture
https://ieeexplore.ieee.org/document/9520758

A Well-Architectured Monolith?

… internally Istio still maintains the logical separation between some of its original control plane components and … each capability is exposed as a discrete API.
The Monolith Strikes Back: Why Istio Migrated From Microservices to a Monolithic Architecture
https://ieeexplore.ieee.org/document/9520758

The “Perforated Monolith”

[Keep] all of the [microservices architecture] principles* … in the same monolithic app …. The result will be a monolith that is baked to perfection and ready to be carved up later.

* “clean separation of concerns, loosely coupled and highly independent services, and consistent interfaces”

Jason Katzer, Learning Serverless (O'Reilly, 2020)

“Perforated” Architecture

A perforated monolith must still be mindful of architectural concerns at the “perforations”.
  • Use timeouts.
  • Detect errors.
  • Provide fallback default values if appropriate.
  • Use e.g. CompletableFuture<T>.

Perforation Mindfulness

“Hello World” Application Logic

//`messageService` injected into app constructor
try {
  messageService.getGreeting()  //`CompletableFuture<String>`
      .completeOnTimeout("Hi, Everybody!", 5, SECONDS);
      .thenAccept(System.out::println)
      .join();  //wait for completion before exiting app
} catch(CompletionException completionException) {
  Throwable cause = completionException.getCause();
  System.err.println(cause.getMessage());
}

But how do I …

… (with a perforated monolith) …

  • 🤔 allow teams to independently develop services?
  • 🤔 independently scale services?
  • 🤔 independently deploy services?

Flange Cloud manages the monolith perforations.

  • 🥳 Monolith services independently developable.
  • 🥳 Monolith services independently scalable.
  • 🥳 Monolith services independently deployable.

Example: “Hello User”

A “plain old monolith application” (POMONA).

“Hello User” Components

Hello User (FaaS): Classes

“Hello User” Projects

Interfaces in separate (sub)projects.
Hello User (FaaS): Projects

“Hello User” Project Structure

hello-user/
├── app/
├── message-service-api/
├── message-service-impl/
├── user-service-api/
└── user-service-impl/

MessageService Interface

public interface MessageService {
  /**
   * Returns greeting to a user.
   * @param username User for which the message is intended.
   * @return A greeting message to the user.
   * @throws CompletionException wrapping an
         {@link IllegalArgumentException} if no such user.
   */
  CompletableFuture<String>
      getGreetingForUser(String username);
}

“Hello User” Application

//`messageService` injected into app constructor
try {
  messageService.getGreetingForUser(username)
      .completeOnTimeout("Howdy!", 10, SECONDS)
      .thenAccept(System.out::println)
      .join();  //wait for completion before exiting app
} catch(CompletionException completionException) {
  Throwable cause = completionException.getCause();
  System.err.println(cause.getMessage());
}

UserService Interface

public interface UserService {
  /**
   * Findsprofile for user with username.
   * @param username User for which profile will be retrieved.
   * @return The user's profile or empty.
   */
  CompletableFuture<Optional<UserProfile>>
      findUserProfileByUsername(String username);
}

MessageServiceImpl

//`userService` injected into `MessageServiceImpl`
public CompletableFuture<String>
    getGreetingForUser(String username) {
  return userService.findUserProfileByUsername(username)
      .thenApply(userProfile -> userProfile
          .orElseThrow(new IllegalArgumentException::new))
      .thenApply(user -> "%s %s"
          .formatted(user.firstName(), user.lastName()))
      .completeOnTimeout(username, 5, SECONDS)
      .thenApply("Hello, %s!"::formatted);
}

Run “Hello User” Locally

Within IDE or Using Flange

Main Class
dev.flange.example.cloud.hellouser_faas.app.HelloUserFaasApp
flange.sh exec \
    dev.flange.example.cloud.hellouser_faas.app.HelloUserFaasApp

Output

Hello, Jay Doe!

Annotate the Perforations

@ServiceConsumer(MessageService.class)
public class HelloUserFaasApp …
@CloudFunctionApi
public interface MessageService …
@CloudFunctionService
@ServiceConsumer(UserService.class)
public class MessageServiceImpl implements MessageService …
@CloudFunctionApi
public interface UserService …
@CloudFunctionService
public class UserServiceImpl implements UserService …

This tells Flange where the perforations are and what you want the perforations to become in the cloud.

Deploy to Cloud

flange.sh cloud deploy dev

This assumes you have performed a one-time setup of a CloudFormation stack for a deployment environment named dev. You can do this quickly using setup/cloud/aws/setup-env.sh. Alternatively you can also set up your own custom cloud environment, as long as the stack name conforms to conventions and the stack provides the expected parameters.

Run “Hello User” in the Cloud

Within IDE or Using Flange

Main Class
dev.flange.example.cloud.hellouser_faas.app.HelloUserFaasApp
Program Arguments
--flange-platform aws --flange-env dev
flange.sh exec -- \
    dev.flange.example.cloud.hellouser_faas.app.HelloUserFaasApp \
    --flange-platform aws --flange-env dev

Output

Hello, Jay Doe!

Architectural Considerations

A perforated monolith must still be mindful of architectural concerns at the “perforations”.
  • Place service interfaces and implementations in separate (sub)projects.
  • Mind the “Fallacies of Distributed Computing”*. The network (at the “perforations”) has latency and is not reliable.
  • The database can still be a bottleneck. Prefer not to share databases among services. Consider using a serverless database service.
* See Fallacies of Distributed Computing Explained by Arnon Rotem-Gal-Oz.
Copyright © 2023 GlobalMentor, Inc. — Presentation framework reveal.js.