Scala cake pattern

Be simple, look stylish

λ.eranga
λ.eranga
Jan 31, 2016 · 5 min read

Background

Following are the main areas that I’m gonna discuss in this post. More theoretical information about the cake pattern can be found in the Scalable Component Abstractions research paper.

  1. Scala Traits
  2. Self typed annotation
  3. Cake pattern with layered components
  4. Dependency injection via cake pattern

All the source codes which related to this posts available at gitlab. Please clone the repo and continue the post.

1. Traits

1.1 Traits vs Interfaces

1.2 Traits vs Abstract classes

1.3 Traits for multiple inheritance

If there are multiple implementors of a given member, the implementation in the super type that is furthest to the right (in the list of super types) wins.

2. Self typed annotation

2.1 Usage

In this scenario we mix Lambda trait with Calculus trait via self typed annotation.

this: Lambda 

The meaning of above expression is Calculus trait requires Lambda trait. When instantiating Calculus trait we have to provide a Lambda trait(trait mixing). Other wise it will gives compilation error. Following is the way that trait mixing happens on instantiating.

val universe = new Calculus with Lambda

2.2 Self typed annotation vs Inheritance

Yes we can. Following is the way we implement above scenario via inheritance.

But there are some problems with using inheritance.

  1. Subclassing will leaks the functionality of super class
  2. Inheritance breaks encapsulations (Its often said that :))

2.3 Why inheritance is bad

Main thing to notice here is. Calculus requires Lambda. Turing Only requires Calculus not requires Lambda. If we access members of Lambda from the Turing it will be a compilation failure.

How could we achieve this trait mixing via inheritance?

In here by extending Calculus trait from Turing, Turing can access Lambda trait members(Self typed annotation prevents that.). Thats why we say’s inheritance leaks the encapsulation.

3. Cake pattern

System = Composition of multiple components

To build a component it uses traits. Component consists with multiple layers. Because of the layered architecture of a component, it named as Cake-Pattern.

Components are injecting(mixing) to other components via self typed annotation. We call it dependency injection.

Dependency injection is only a one feature which can achieve via cake pattern. Main purpose of having cake pattern is building layered components.

3.1 Component

A system deals with employees. User can enter employees via command line. Then the system validates the entered employee and save it in the database. Finally employee will uploaded to cloud(via REST API).

If we apply cake pattern to build this system, first we have to identifies the components. In here we can have two main components.

  1. Employee DB component(Which deals with get, insert, update, delete employee records via the database)
  2. Employee Service component(Which deals with POST employee details to cloud REST service)

3.2 Employee DB component

Actual database related functions comes with EmployeeDb trait. The functionality of the EmployeeDb wraps with EmployeeDbComp trait. We gives a single entry point to EmployeeDb trait from EmployeeDbComp trait

val employeeDb: EmployeeDB

EmployeeDb component is an abstract component. We can have multiple implementations of this component. For an instance we can have

  1. Appache cassandra based EmployeeDb component
  2. MongoDB based EmployeeDb component
  3. Mocked EmployeeDb component for unit tests
  4. etc

Following is the cassnadra based EmployeeDb component implementation.

3.3 Employee Service Component

Following is the Spray based implementation of the Employee Service.

4. Dependency injection

4.1 EmployeeHandler

Assume there is a handler class(EmployeeHandler) which directly takes user inputs, validates them, and manages employee creation(in database) and uploading. In order to creates and upload employees, handler class needs to have a dependencies to EmployeeDbComp and EmployeeServiceComp. Following is the EmployeeHandler implementation

We can injecting dependencies to EmployeeHandler via self typed annotations.

this: EmployeeDbComp with EmployeeServiceComp =>

When instantiating the employee handler we have to provide EmployeeDbComp and EmployeeServiceComp. Following is the way to do that.

val employeeHandler = new EmployeeHandler with CassandraEmployeeDbComp with SprayEmployeeServiceComp 

In here we are mixing following components with EmployeeHandler

  1. EmployeeDbComp implementation(CassandraEmployeeDbComp)
  2. EmployeeServiceComp implementation(SprayEmployeeServiceComp)

4.2 Component initializing on the fly

Reference

  1. https://kubuszok.com/2018/cake-antipattern/
  2. https://coderwall.com/p/t_rapw/cake-pattern-in-scala-self-type-annotations-explicitly-typed-self-references-explained
  3. https://www.clianz.com/2016/04/26/scala-cake-pattern/
  4. https://github.com/davidmoten/cake-pattern

Rahasak Labs

Have less, be more

Rahasak Labs

Have less, be more

λ.eranga

Written by

λ.eranga

Ego = 1/Knowledge

Rahasak Labs

Have less, be more