scala, the startup way
TRANSCRIPT
The Startup way● Limited resources● Limited time● Fast iterations● Least power principle● Make an effort to understand the trade offs● Consciously evaluate and justify the cost of moving
up/down the abstraction ladder
The spectrum of abstractions● Moving up
○ More powerful○ Less constraints
● Moving down○ More properties○ More constraints○ Closer to the problem
● Moving cost● Cognitive cost● Least power principle● Abstraction overfitting
Reference: https://www.youtube.com/watch?v=mVVNJKv9esE
Example: Vim v.s. Emacs
● Emacs is more powerful● Vim is more specific● Abstraction level● Usage pattern
Emacs
Vim
Browse MassagePlay music GTD Editing
Example: Static v.s.Dynamic typing
● Static typing○ More constraints○ Compile time checking○ Should alway use it! :)○ Unless it gets in our way
● Dynamic typing○ Less constraints○ More flexible / powerful
Dynamic typing
Static typing
Data types
● Built-in types● Algebraic data types
○ case class○ sealed trait
● Functions● Ad-hoc classes
○ Ubiquitous in Java
Ad-hoc classes
ADT Functions
Built-in types
Reference: http://www.lihaoyi.com/post/StrategicScalaStylePrincipleofLeastPower.html
Asynchronous Programming model● Data
○ Single element○ Stream of elements
● Topology○ Unidirectional edge○ Complex graph
● Location○ Local○ Distributed
● Type safety○ Typed○ Untyped
Asynchronous Programming model● Future
○ An object that acts like a proxy for an initially unknown result○ Typed○ Composable○ Bounded timeout○ Unidirectional○ Treating data as its entirety○ Local abstraction
Asynchronous Programming model● Akka Stream
○ Complex graph, typed
○ Composable○ Back pressure○ Treating data as stream of elements○ Local abstraction
final class Source[+Out, +Mat] extends Graph[SourceShape[Out], Mat]
final class Sink[-In, +Mat] extends Graph[SinkShape[In], Mat]
final class Flow[-In, +Out, +Mat] extends Graph[FlowShape[In, Out], Mat]
Asynchronous Programming model● Akka actors
○ Message passing with internal state, very expressive○ Location transparency, distribution○ Complex graph, untyped (experimental akka typed)
○ Not composable (because of the signature above)○ Treating data as stream of elements (messages) but hard to achieve
stable streaming (bounded mailbox)○ Opacity of the actors makes it hard to reason about complex programs
type Receive = PartialFunction[Any, Unit]
def tell(msg: Any, sender: ActorRef): Unit
Asynchronous Programming model● We use Future extensively
○ use scala-async if too much control flows
● Streams for things like websockets, bank synchronization● Haven’t come across the case of using Akka Actor yet
Dependency Injection
● Assemble components● Separate dependency
creation and behavior● Encourage modularityManual injection
Guice
Macwire
Spring
Dependency Injection● Manual DI
○ No magic, simple to understand○ Static type checking for dependency graph○ Flexible enough for expressing various injection strategies○ Might be tedious○ Roll your own if in need of “advanced” DI features
class Services {
val foo = new Foo()
val bar = new Bar()
val doo = new Doo(foo, bar)
}
Dependency Injection● Have to be lazy, sometimes
● Sometimes being lazy isn’t enough
class Services {
val foo = new Foo()
lazy val doo = new Doo(foo, bar)
lazy val bar = new Bar()
}
class Services {
lazy val foo = new Foo(bar)
lazy val bar = new Bar(foo)
}
Dependency Injection● Using implicits
○ Remove some of the tediousness○ Had to declare implicits as parameters, which could be confusing
● Thin cake○ Use Traits to separate concerns ○ Make it “feel” like less tedious○ Manage name conflicts
trait FooModule { val foo = new Foo() }
trait BarModule { val bar = new Bar() }
trait DooModule {
val doo = new Doo(foo, bar)
val foo: Foo
val bar: Bar
}
class Services {
val modules = new DooModule
extends FooModule
with BarModule
}
Dependency Injection● Guice
○ Address tediousness with annotations or bind○ Things are done at runtime
@implementedBy(classOf[FooImpl])
trait Foo { ??? }
@implementedBy(classOf[BarImpl])
trait Bar { ??? }
val injector = Guice.createInjector();
val doo = injector.getInstance(classOf[Doo]);
● Spring○ Address tediousness with annotations or applicationContext.xml○ Abstraction is so heavy that it is hard to know what it does○ Things are done at runtime
Dependency Injection● Macwire
○ Use macro to automatically remove tediousness of manual DI○ Static type checking of dependency graphs○ Features such as interceptors○ Doesn’t do much magic○ Big enough benefits to introduce it?
import com.softwaremill.macwire._
class Services {
val foo = wire[Foo]
val bar = wire[Bar]
val doo = wire[Doo]
}
Dependency Injection● Stick with manual DI since due to
○ Simplicity○ Easy to understand / debug○ Level of tediousness is tolerable○ Not in need of more advanced DI features
● Thin cake?● Macwire?
Storage model● Constantly evolving data model● Analyse data● Traceability● Handle sensitive data
Event Sourcing● Events
○ All updates that can be performed on a model is through events
● Views○ Projection of events○ Can have multiple
● Constrained by the events○ Append only
Event Sourcingsealed trait ProductEvent
case class ProductCreated(productId: ProductId, supplierId: SupplierId, price: Price, at: DateTime)
extends ProductEvent
case class ProductPriceChanged(productId: ProductId, price: Price, at: DateTime) extends ProductEvent
case class ProductDeleted(productId: ProductId, at: DateTime) extends ProductEvent
case class ProductView(id: ProductId, supplierId: SupplierId, price: Price, state: ProductState,
createdAt: DateTime)
ExampleProductCreated (productId, supplier, Price(10), DateTime("2017-02-28 12:00:00" ))
=> ProductView(productId, supplier, Price(10), Active, DateTime("2017-02-28 12:00:00" ))
ExampleProductCreated (productId, supplier, Price(10), DateTime("2017-02-28 12:00:00" ))
=> ProductView(productId, supplier, Price(10), Active, DateTime("2017-02-28 12:00:00" ))
ProductPriceChanged (productId, Price(15), DateTime("2017-02-28 13:30:00" ))
=> ProductView(productId, supplier, Price(15), Active, DateTime("2017-02-28 12:00:00" ))
ExampleProductCreated (productId, supplier, Price(10), DateTime("2017-02-28 12:00:00" ))
=> ProductView(productId, supplier, Price(10), Active, DateTime("2017-02-28 12:00:00" ))
ProductPriceChanged (productId, Price(15), DateTime("2017-02-28 13:30:00" ))
=> ProductView(productId, supplier, Price(15), Active, DateTime("2017-02-28 12:00:00" ))
ProductDeleted (productId, DateTime("2017-02-28 14:00:00" ))
=> ProductView(productId, supplier, Price(15), Deleted, DateTime("2017-02-28 12:00:00" ))
CRUD● Mutable changes
○ Update○ Delete
● Lower cost without traceability
○ Use when no benefits of event sourcing
● Event sourcing is more constrained
Event sourcing
CRUD
Deployment● Easy to deploy● Deploy often● Scaleable platform● Controlled releases
Docker containers
Docker compose
Kubernetes
Deployment
Deploying
InTo
Kubernetes
Health status
Showing logs
Deployment
Demo
Like Scala?Like scaling up systems?Love to create user value?