Functional architecture The pit of success Mark Seemann

  • Slides: 69
Download presentation
Functional architecture The pit of success Mark Seemann http: //blog. ploeh. dk @ploeh

Functional architecture The pit of success Mark Seemann http: //blog. ploeh. dk @ploeh

@ploeh

@ploeh

@ploeh

@ploeh

@ploeh

@ploeh

@ploeh

@ploeh

@ploeh

@ploeh

Parallelism Ports and adapters Services, Entities, Value Objects Testability @ploeh

Parallelism Ports and adapters Services, Entities, Value Objects Testability @ploeh

PARALLISM

PARALLISM

public void Update() { // when a generation has completed // now flip the

public void Update() { // when a generation has completed // now flip the back buffer so we can start // processing on the next generation var flip = this. next. Generation; this. next. Generation = this. world; this. world = flip; Generation++; this. Process. Generation(); } @ploeh

public void Update() { lock(this. sync. Lock) // when a generation has completed //

public void Update() { lock(this. sync. Lock) // when a generation has completed // now flip the back buffer so we can start // processing on the next generation var flip = this. next. Generation; this. next. Generation = this. world; this. world = flip; Generation++; this. Process. Generation(); } @ploeh

public void Update() { lock(this. sync. Lock) // when a generation has completed //

public void Update() { lock(this. sync. Lock) // when a generation has completed // now flip the back buffer so we can start // processing on the next generation var flip = this. next. Generation; this. next. Generation = this. world; this. world = flip; Generation++; this. Process. Generation(); } @ploeh

public void Update() { lock(this. sync. Lock) { // when a generation has completed

public void Update() { lock(this. sync. Lock) { // when a generation has completed // now flip the back buffer so we can start // processing on the next generation var flip = this. next. Generation; this. next. Generation = this. world; this. world = flip; Generation++; this. Process. Generation(); } @ploeh

public void Update() { lock(this. sync. Lock) { // when a generation has completed

public void Update() { lock(this. sync. Lock) { // when a generation has completed // now flip the back buffer so we can start // processing on the next generation var flip = this. next. Generation; this. next. Generation = this. world; this. world = flip; Generation++; this. Process. Generation(); } @ploeh

public void Update() { lock(this. sync. Lock) { // when a generation has completed

public void Update() { lock(this. sync. Lock) { // when a generation has completed // now flip the back buffer so we can start // processing on the next generation var flip = this. next. Generation; this. next. Generation = this. world; this. world = flip; Generation++; this. Process. Generation(); } @ploeh

public void Update() { lock(this. sync. Lock) { // when a generation has completed

public void Update() { lock(this. sync. Lock) { // when a generation has completed // now flip the back buffer so we can start // processing on the next generation var flip = this. next. Generation; this. next. Generation = this. world; this. world = flip; Generation++; this. Process. Generation(); } } @ploeh

let tick live. Cells = let is. Alive = function Alive -> true |

let tick live. Cells = let is. Alive = function Alive -> true | _ -> false live. Cells |> Seq. collect find. Neighbors |> Seq. distinct |> Seq. filter (calculate. Next. State live. Cells >> is. Alive) |> Seq. to. Array |> Set. of. Array @ploeh

let tick live. Cells = let is. Alive = function Alive -> true |

let tick live. Cells = let is. Alive = function Alive -> true | _ -> false live. Cells |> PSeq. collect find. Neighbors |> PSeq. distinct |> PSeq. filter (calculate. Next. State live. Cells >> is. Alive) |> PSeq. to. Array |> Set. of. Array @ploeh

PORTS AND ADAPTERS

PORTS AND ADAPTERS

@ploeh

@ploeh

Same return value for same input Pure function No sideeffects @ploeh

Same return value for same input Pure function No sideeffects @ploeh

Impure function Pure function @ploeh

Impure function Pure function @ploeh

validate. Reservation : : Reservation. Rendition -> Either Error Reservation validate. Reservation r =

validate. Reservation : : Reservation. Rendition -> Either Error Reservation validate. Reservation r = case parse. Date (r. Date r) of Just d -> Right Reservation { date = d , name = r. Name r , email = r. Email r , quantity = r. Quantity r } Nothing -> Left (Validation. Error "Invalid date. ") @ploeh

get. Reserved. Seats. From. DB : : Connection. String -> Zoned. Time -> IO

get. Reserved. Seats. From. DB : : Connection. String -> Zoned. Time -> IO Int @ploeh

check. Capacity : : Int -> Reservation -> Either Error Reservation check. Capacity capacity

check. Capacity : : Int -> Reservation -> Either Error Reservation check. Capacity capacity reserved. Seats reservation = if capacity < quantity reservation + reserved. Seats then Left Capacity. Exceeded else Right reservation @ploeh

save. Reservation : : Connection. String -> Reservation -> IO () @ploeh

save. Reservation : : Connection. String -> Reservation -> IO () @ploeh

to. Http. Result : : Either Error () -> Http. Result () to. Http.

to. Http. Result : : Either Error () -> Http. Result () to. Http. Result (Left (Validation. Error msg)) = Bad. Request msg to. Http. Result (Left Capacity. Exceeded) = Status. Code Forbidden to. Http. Result (Right ()) = OK () @ploeh

post. Reservation : : Reservation. Rendition -> IO (Http. Result ()) post. Reservation candidate

post. Reservation : : Reservation. Rendition -> IO (Http. Result ()) post. Reservation candidate = fmap to. Http. Result $ run. Either. T $ do r <- hoist. Either $ validate. Reservation candidate i <- lift. IO $ get. Reserved. Seats. From. DB conn. Str $ date r hoist. Either $ check. Capacity 10 i r >>= lift. IO. save. Reservation conn. Str @ploeh

check. Capacity @ploeh

check. Capacity @ploeh

let imp candidate = either { let! r = Validate. reservation candidate let i

let imp candidate = either { let! r = Validate. reservation candidate let i = Sql. Gateway. get. Reserved. Seats connection. String r. Date let! r = Capacity. check 10 i r return Sql. Gateway. save. Reservation connection. String r } new Reservations. Controller(imp) : > _

SERVICES AND DATA

SERVICES AND DATA

public class User { public int Id { get; } public string User. Name

public class User { public int Id { get; } public string User. Name { get; } public static User Create. New(string user. Name) public static User Read(int id) public void Update() public void Delete() } @ploeh

public class User { public int Id { get; } public string User. Name

public class User { public int Id { get; } public string User. Name { get; } public static User Create. New(string user. Name) public static User Read(int id) public void Update() public void Delete() } @ploeh

public class User { public int Id { get; } public string User. Name

public class User { public int Id { get; } public string User. Name { get; } public static User Create. New(string user. Name) public static User Read(int id) public void Update() public void Delete() public void Send. Email(Email message) } @ploeh

@ploeh

@ploeh

Entities Services Value Objects @ploeh

Entities Services Value Objects @ploeh

public class User { public User(int id, string user. Name) public int Id {

public class User { public User(int id, string user. Name) public int Id { get } public string User. Name { get } } @ploeh

public class Sql. User. Repository : IUser. Repository { public User Create. New(string user.

public class Sql. User. Repository : IUser. Repository { public User Create. New(string user. Name) public User Read(int id) public void Update(User user) public void Delete(User user) } @ploeh

public class Email. Sender : ISend. Email { public void Send. Email. To(User user,

public class Email. Sender : ISend. Email { public void Send. Email. To(User user, Email message) } @ploeh

Anaemic Model @ploeh

Anaemic Model @ploeh

Anaemic Model @ploeh

Anaemic Model @ploeh

Anaemic D Model @ploeh

Anaemic D Model @ploeh

Anaemic Do Model @ploeh

Anaemic Do Model @ploeh

Anaemic Dom Model @ploeh

Anaemic Dom Model @ploeh

Anaemic Doma Model @ploeh

Anaemic Doma Model @ploeh

Anaemic Domai Model @ploeh

Anaemic Domai Model @ploeh

Anaemic Domain Model @ploeh

Anaemic Domain Model @ploeh

Entities Services Value Objects @ploeh

Entities Services Value Objects @ploeh

Data Functions @ploeh

Data Functions @ploeh

type User = { Id : int; User. Name : string } @ploeh

type User = { Id : int; User. Name : string } @ploeh

module Persistence = // string -> User let create. New user. Name = //.

module Persistence = // string -> User let create. New user. Name = //. . // int -> User let read id = //. . // User -> unit let update user = //. . // User -> unit let delete user = //. . @ploeh

module Mail = // User -> Email -> unit let send. Email. To user

module Mail = // User -> Email -> unit let send. Email. To user message = //. . @ploeh

public sealed class Money { public Money(decimal amount, string currency) public decimal Amount {

public sealed class Money { public Money(decimal amount, string currency) public decimal Amount { get } public string Currency { get } public override bool Equals(object obj) public override int Get. Hash. Code() } @ploeh

type Money = { Amount : decimal; Currency : string } > { Amount

type Money = { Amount : decimal; Currency : string } > { Amount = 1 m; Currency = "EUR" } = { Amount = 1 m; Currency = "EUR" }; ; val it : bool = true > { Amount = 2 m; Currency = "EUR" } = { Amount = 1 m; Currency = "EUR" }; ; val it : bool = false > { Amount = 1 m; Currency = "EUR" } = { Amount = 1 m; Currency = "GBP" }; ; val it : bool = false @ploeh

TESTABILITY

TESTABILITY

Test-induced damage https: //www. flickr. com/photos/davehamster/8667991007 @ploeh

Test-induced damage https: //www. flickr. com/photos/davehamster/8667991007 @ploeh

public class Capacity. Checker : ICapacity. Checker { private readonly int capacity; private readonly

public class Capacity. Checker : ICapacity. Checker { private readonly int capacity; private readonly IReservation. Repository repo; public Capacity. Checker(int capacity, IReservation. Repository repo) { this. capacity = capacity; this. repo = repo; } public bool Has. Capacity(Reservation reservation) { var reserved = this. repo. Get. Reserved. Seats(reservation. Date); return this. capacity < reservation. Quantity + reserved; } } @ploeh

let check capacity get. Reserved. Seats reservation = let reserved. Seats = get. Reserved.

let check capacity get. Reserved. Seats reservation = let reserved. Seats = get. Reserved. Seats reservation. Date if capacity < reservation. Quantity + reserved. Seats then Failure Capacity. Exceeded else Success reservation @ploeh

[<Fact>] let ``check returns right result at no prior reservations`` () = let capacity

[<Fact>] let ``check returns right result at no prior reservations`` () = let capacity = 10 let get. Reserved. Seats _ = 0 let reservation = { Date = Date. Time. Offset(Date. Time(2014, 8, 10), Time. Span. From. Hours 2. ) Name = "Mark Seemann" Email = "mark@ploeh. dk" Quantity = 4 } let actual = Capacity. check capacity get. Reserved. Seats reservation let expected : Result<Reservation, Error> = Success reservation test <@ expected = actual @> @ploeh

Jessica Kerr Isolation When the only information a function has about the external world

Jessica Kerr Isolation When the only information a function has about the external world is passed into it via arguments. https: //twitter. com/jessitron @ploeh

Isolation Pure function @ploeh

Isolation Pure function @ploeh

Unit Test A unit test is an automated test that tests a unit in

Unit Test A unit test is an automated test that tests a unit in isolation from its dependencies. @ploeh

Unit Test A unit test is an automated test that tests a unit in

Unit Test A unit test is an automated test that tests a unit in isolation from its dependencies. @ploeh

Isolation Testinduced damage OOP @ploeh

Isolation Testinduced damage OOP @ploeh

Encapsulation Testability Object-Oriented Programming Isolation @ploeh

Encapsulation Testability Object-Oriented Programming Isolation @ploeh

Functional Programming Testable Isolated Ideal function @ploeh

Functional Programming Testable Isolated Ideal function @ploeh

@ploeh

@ploeh

@ploeh

@ploeh

Parallelism Ports and adapters Services, Entities, Value Objects Testability @ploeh

Parallelism Ports and adapters Services, Entities, Value Objects Testability @ploeh

Mark Seemann http: //blog. ploeh. dk http: //bit. ly/ploehralsight @ploeh

Mark Seemann http: //blog. ploeh. dk http: //bit. ly/ploehralsight @ploeh