Scase
Run functional Scala code as a portable serverless function or microservice
Install / Use
/learn @jobial-io/ScaseREADME
Scase
Run Scala or Java code as a portable serverless function or microservice
Zero boilerplate and complete type safety
Integrate Message-Oriented Middlewares and protocols easily
Uniform and portable messaging API for your applications
<br/>Scase is a lightweight functional library that bridges the gap between microservice platforms, messaging APIs and functional Scala code.
Scase helps you achieve
- Quick implementation and deployment of serverless functions or microservices
- Type-safe, functional implementation of common messaging patterns in enterprise applications
- Uniform API for a wide range of messaging protocols and middelware
- Clean separation of service API, implementation and client side
- Portability: deployment on multiple platforms such as AWS Lambda, Kafka or Apache Pulsar without rewriting application code
- Clean, purely functional internal design and API
- Pluggable protocol support
- High performance
Motivation
Mircoservices and messaging are fundamental building blocks of many applications. From an application developer point of view, the use cases are often similar, yet the underlying APIs and runtimes can be quite different. In many cases, integration and deployment requires a substantial effort and a lot of boilerplate code. Application code usually outlives these runtimes and protocols and sometimes the same code has to run on multiple runtimes at the same time (e.g. local execution, cloud deployment, testing, migration...). Scase provides a clean and simple way to address these use cases while keeping the application code portable and clean.
Serverless functions
When thinking of implementing a microservice or a serverless function, we typically want to do something like:
// Service code:
case m: MyRequest =>
// do processing in some effect F and then:
m.reply(MyResponse(...))
case m: MyOtherRequest =>
...
and on the client side:
// Client code:
myClient ? MyRequest("hello") // : F[MyResponse]
We want the code to be as type-safe as possible, with no possibility of replying with the "wrong" type or forgetting to send a response entirely.
Application developers like to focus on the business logic, implemented on top of a safe, concise and platform independent API.
It is usually not important if the service is eventually deployed as an AWS Lambda, an Apache Pulsar function or a standalone app in a container, or maybe run in an Akka Cluster, or in a test locally.
In addition, we would like to:
-
Be able to access services from anywhere, in a type-safe way, using a uniform API
-
Decouple the business logic from frameworks like Akka or AWS Lambda
-
Be able to write idiomatic, functional Scala code for the service logic, completely decoupled from the underlying implementation
-
Use concurrency seamlessly and safely
-
Still be able to access common features of messaging and serverless runtimes if needed (e.g. message attributes, commit / rollback).
Scase makes these available through a concise, platform independent API, with the benefit of:
- Maximum type safety and no boilerplate
- Portable code between deployment and runtime environments, without rewriting
- Support for deployment on a range of runtime environments, like AWS Lambda, SQS, SNS, Akka, Apache Pulsar, Kafka or standalone app
- Simple, future proof, platform independent code for your application logic
- Integration with Cloudformation
- Easily extendable support for serialization and network protocols, with built-in support for Circe, Spray Json, Java serialization and others
- Integrated with effect systems like Cats Effect, ZIO, Monix (can also be used seamlessly with Scala / Java Futures)
- Lightweight, modular, extendable design that provides a simple layer between runtime and application code - not a " framework"
- Java-friendly DSL for clients and services
- Test support
- Well defined error handling
- Purely functional, from top to bottom, but without the need to understand complex FP constructs.
Additionally, Scase does not enforce any specific convention of modelling messages or correlating request and response types. It comes with sensible defaults, with pluggable support for custom styles.
Scase can also be used as a simple, functional alternative to native messaging APIs (e.g. for Apache Pulsar, Kafka, SQS and so on).
An example
A simple greeting service:
import cats.effect.IO
import io.jobial.scase.core._
trait GreetingService extends RequestHandler[IO, GreetingRequest[_ <: GreetingResponse], GreetingResponse] {
def handleRequest(implicit context: RequestContext[IO]) = {
case m: Hello =>
m ! HelloResponse(s"Hello, ${m.person}!")
case m: Hi =>
for {
_ <- IO(println(s"processing request $m..."))
} yield m ! HiResponse(s"Hi ${m.person}!")
}
}
Full example can be found at scase-pulsar-example.
A few things to highlight:
- The service must handle every message, it is a compile time error if a request is not replied appropriately
- The request must be replied using the right type, again, it is checked at compile time
- The response message on the client side is type-safe; for example, for
Hellothe client code receives aHelloResponseand forHithe response type isHiResponse - It is not possible to send a request that does not conform to the client's type, this is checked at compile time.
- It is optional to use Cats Effect's
IO: you can use any other effect type. If you are not familiar with functional effects or need to use non-pure code, you can just wrap your code in anIO.
How to use
You need to add
libraryDependencies ++= Seq(
"io.jobial" %% "scase" % "2.2.1""
)
to build.sbt or
<dependency>
<groupId>io.jobial</groupId>
<artifactId>scase_${scala.version}</artifactId>
<version>2.2.1</version>
</dependency>
to pom.xml if you use Maven, where scala.version is either 2.12 or 2.13. The 2.13 artifacts are Scala 3.x compatible. For Scala 2.11, please use the 1.x versions.
Effect types
Cats Effect
Scase is built around the "tagless final" pattern throughout using Cats type classes, which makes the API as well as the internal implementation agnostic to the effect type chosen by the application developer. It supports the effect types provided by Cats Effect (IO) as well as ZIO out of the box. Other effect types can easily be plugged into the library as long as they support the minimum requirements (usually Concurrent and Timer instances have to be available for the effect).
ZIO
ZIO is supported seamlessly through ZIO cats-interop:
Integrations
AWS Lambda
TODO: add explanation on AWS env
AWS SQS
AWS SNS
...
AWS CloudFormation
Apache Pulsar
Kafka
...
Akka
...
JMS
...
Local
Tibco Rendezvous
...
Messaging patterns
Request-response
...
One-way
...
Stream processing
...
Routing
...
Transactional transports
If the underlying transport supports transactional messaging (SQS, for example), Scase exposes this functionality through the Consumer and Producer APIs.
What about HTTP?
But wait, aren't microservices just HTTP / REST services? In many enterprise environments this is not the case or the preferred solution. HTTP is very constrained in comparison to general asynchronous message passing. A good reading on the topic is:
Of course, nothing prevents anyone from deploying a Scase service as an HTTP endpoint. Also, many messaging solutions implement the underlying message passing over HTTP (e.g. AWS Lambda, SQS, SNS).
Java DSL
Scase provides a Java DSL to allow seamless and idiomatic usage in Java.
Of course, any third party client can talk to a Scase service and vice versa, a Scase client can call a non-Scase service.
Core concepts
Client API
...
Service API
...
Marshalling
Marshalling / unmarshalling is done using the Marshaller and Unmarshaller type classes. Scase provides implementation for many popular serialization formats and libraries:
- Circe (JSON) (Example)
- Java serialization
- Raw bytes
- Spray JSON ([Example](https://github.com/jobial-io/scase-spra
Related Skills
gh-issues
347.0kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
node-connect
347.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
oracle
347.0kBest practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).
taskflow-inbox-triage
347.0kname: taskflow-inbox-triage description: Example TaskFlow authoring pattern for inbox triage. Use when messages need different treatment based on intent, with some routes notifying immediately, some w
