Architecting a web application in a functional style

 

Chris Birchall

Agenda

  • Hand waving
  • Write some code
  • Summarise

http://lambdale.org/

@lambd_ale

1st September 2018

Functional style?

Obstacles to writing in functional style

  • Disconnect between theory and practice
  • Decision paralysis
  • Nagging doubts

Disconnect between theory and practice

Disconnect between theory and practice

theory

practice

Kan

extensions

corepresentable functors

Monads

Yoneda lemma

Type classes

Kleisli

StateT

"build a working, well-tested, maintainable piece of software to deliver business value"

Decision paralysis

 

Somebody told me to use free monads,

but somebody else told they were passé

 

Should this thing be a class or an object?

Is it ok to use traits?

 

I don't even know what my package structure

should look like

Nagging doubt #1

 

Am I doing it right?

Nagging doubt #2

 

Is FP actually better than the alternatives?

Benefits of FP

  • Local reasoning
  • Abstraction
  • Composition

Nagging doubt #3

 

Don't FP and OOP boil down to the same thing anyway?

Yes!

 

But one or the other might still be

a better tool for the job

Worked example

 

Photo storage service

Requirements

 

User can upload a photo and get back a unique ID

 

User can retrieve a photo by ID

Detailed requirements: upload photo

 

  • Check that uploaded data is a valid photo
    • If validation fails, log it and return a 400 error
  • Store to a durable filestore service
    • If this fails, log a warning and return a 500 error
  • Also store to a remote cache
    • If this fails, just log a warning
  • Send a "photo uploaded" Kafka event
  • Return a 201 response containing the photo ID

Detailed requirements: retrieve photo


  • First lookup the file in the cache
    • If this fails, log a warning and treat as cache miss
  • On cache hit, return a 200 response
  • On cache miss:
    • Retrieve file from filestore
      • If file exists:
        • Write it to the cache
          • If this fails, just log a warning
        • Return a 200 response
      • Else, return a 404 response

Non-functional requirements

 

  • Request ID should be extracted from request header (or generated if not present)
    • Should be propagated to all backend requests
    • Should be included in all logs
  • We expect a lot of concurrent requests, so all I/O must be asynchronous

Domain-driven design

Domain

Translation

External world

Functional design

Pure

Impure

External world

Functional design

Abstract

Concrete

External world

"DDD-inspired" package structure

api
application
domain
infrastructure

HTTP controllers

Concrete effects, interpreters

Domain models and algebras

HTTP clients, config loading, ...

Functional domain modelling

Data     Behaviour = Domain

ADTs     Algebras   = Domain

+

+

Errors

  • Expected errors

    • Part of the domain
    • Model with Either
  • Unexpected errors
    • Network errors, cosmic rays, ...
    • Handle with Try, IO, Task, ...
  • In general you need to handle both
    • EitherT can be handy
def validate(input: Foo): IO[Either[ValidationError, Bar]]

Enough talk!

Let's write some code

Summary

✓ Local reasoning

 

Abstraction

 

Composition

Architecting a web application in a functional style

By Chris Birchall

Architecting a web application in a functional style

  • 2,658