Sourcing de eventos utilizando la programación funcional -- f# campo con ddd camp codereview Relacionados El problema

Event sourcing using functional programming


5
vote

problema

Español

¿Es esta una implementación satisfactoria del abastecimiento de eventos utilizando la programación funcional?

  doDamage()2  
Original en ingles

Is this a satisfactory implementation of event sourcing using functional programming?

module Account  (*Types*) type EventAggregate<'Aggregate> = { Event:Event; Aggregate:'Aggregate }  and Command =      | Login      of EventAggregate<Account>     | Deposit    of EventAggregate<Account>     | Withdrawal of EventAggregate<Account>  and Event =      | LoggedIn  of Login     | Deposited of decimal     | Withdrew  of decimal  and AccountId = AccountId of string and FirstName = FirstName of string and LastName =  LastName  of string  and Login =   { UserId:string; Password:string } and Account = {     AccountId: AccountId     FirstName: FirstName     LastName:  LastName }  (*Functions*) let appendTo (repo:Map<Event,_>) (plan:EventAggregate<'a>) =     repo.Add (plan.Event , plan.Aggregate)  let getAccount login = {     AccountId= AccountId "myAccountId"     FirstName= FirstName "Scott"     LastName= LastName   "Nimrod" }  let interpret cmd repo = cmd |> function     | Login   v     | Deposit v -> appendTo repo v           | _         -> repo  (*Client*) let login = { UserId= "MyUserId"; Password= "MyPassword" } let account = getAccount login  let loginPlan =    { Event=     LoggedIn login                      Aggregate= account        }  let depositPlan =  { Event=     Deposited 100m                      Aggregate= account        }  let store =  (Login loginPlan , Map.empty)               ||> interpret               |> interpret (Deposit depositPlan) 
     
 
 

Lista de respuestas

5
 
vote
vote
La mejor respuesta
 

El concepto clave en el abastecimiento de eventos es tener una función "actualizadora" que requiere un evento y un estado antiguo, y devuelve un nuevo estado. Vamos a capturar eso como tipo. El tipo puede ser muy genérico:

  BOARDLENGTH -> BOARD_LENGTH BOARDHEIGHT -> BOARD_HEIGHT 4  

Lo siguiente para capturar es su dominio específico y su implementación específica de la función 'Updater':

  BOARDLENGTH -> BOARD_LENGTH BOARDHEIGHT -> BOARD_HEIGHT 5  

Finalmente, necesitas algún flujo de eventos. Puede ser en forma de algún tipo de datos de transmisión o una secuencia observable. Utilizaremos un 99887766655443316 porque está idiomático en el mundo .NET. Con este flujo de eventos, puede "doblar" sobre los eventos y regenerar su estado final (su cuenta) aplicando la función de actualización en cada paso.

  BOARDLENGTH -> BOARD_LENGTH BOARDHEIGHT -> BOARD_HEIGHT 7  
 

The key concept in event sourcing is having an 'updater' function that takes an event and an old state, and returns a new state. Let's capture that as a type. The type can be very generic:

module Update =   type t<'s, 'e> = 's -> 'e -> 's    (**   A function that represents the operation of taking an event and an old   state to return a new state.   *) 

The next thing to capture is your specific domain and its specific implementation of the 'updater' function:

module Account =   type t = { logged_in : bool; balance : decimal }   type event =     | Login of userid:string * passwd:string     | Deposit of decimal     | Withdraw of decimal    let empty = { logged_in = false; balance = 0m }   let login_valid userid passwd = true // <- TBD   let update t event =     match event, t with       | Login (userid, passwd), _ ->         { t with logged_in = login_valid userid passwd }        | Deposit amt, { logged_in = true; balance = balance } ->         { t with balance = balance + amt }        | Withdraw amt, { logged_in = true; balance = balance } ->         { t with balance = balance - amt }        | _, t -> t    (* val update : Update.t<t, event> 

Finally, you need some stream of events. It can be in the form of some stream data type or an observable sequence. We will use an IObservable<Account.event> because it's idiomatic in the .Net world. With this stream of events, you can 'fold' over the events and regenerate your final state (your account) by applying the update function at each step.

module Main =   open System.Reactive.Linq    (* For example only; real stream will be built differently. *)   let acct_events =     Observable.Concat(       [| Observable.Return(Account.Login ("bob", "pass"))          Observable.Return(Account.Deposit 1000m)          Observable.Return(Account.Withdraw 500m) |])    let acct =     Observable.Aggregate(acct_events, Account.empty, Account.update)    (** val acct : IObservable<Account.t> *) 
 
 
         
         
2
 
vote

Actualizé mi código basado en la retroalimentación de revisión de código.

Mi solución se ha actualizado a lo siguiente:

  BOARDLENGTH -> BOARD_LENGTH BOARDHEIGHT -> BOARD_HEIGHT 8  
 

I updated my code based on code review feedback.

My solution has been updated to the following:

module EventSourcing =      type StateResponse<'state, 'event> = ('state * 'event) -> 'state  module Account =      open EventSourcing      type AccountId = AccountId of string     type FirstName = FirstName of string     type LastName =  LastName  of string      type Credentials =   { UserId:string; Password:string }     type Account = {          AccountId: AccountId          FirstName: FirstName          LastName:  LastName          Balance:   decimal }      and Event =          | LoggedIn  of Credentials         | Deposited of balance: decimal         | Withdrew  of balance: decimal      (*Functions*)     let emptyAccount = {          AccountId= AccountId ""          FirstName= FirstName ""          LastName=  LastName  ""          Balance=   0.00m }      let getAccount credentials = {         AccountId= AccountId "myAccountId"         FirstName= FirstName "Scott"         LastName= LastName   "Nimrod"         Balance=  0.00m                    }      let update : StateResponse<Account,Event> = function         | account , LoggedIn  credentials ->   getAccount credentials         | account , Deposited balance     -> { account with Balance = account.Balance + balance }         | account , Withdrew  balance     -> { account with Balance = account.Balance - balance }      (*Client*)     let credentials = { UserId="Bizmonger"; Password="MyPassword" }     let account =       credentials |> getAccount      let events =         [             LoggedIn  credentials             Deposited 1000m             Deposited 5000m             Withdrew  500m         ]      let nextUpdate account event = update (account , event)     let hydratedAccount          = (emptyAccount , events)                                     ||> List.fold nextUpdate 
 
 

Relacionados problema

1  Modelado de capa de dominio para sistema de flujo de trabajo básico  ( Modeling domain layer for basic workflow system ) 
Como hasta ahora, he estado desarrollando principalmente productos existentes y realizando mantenimiento / ingeniería, me siento necesaria para modelar algo d...

1  Revise el repositorio para una entidad anidada  ( Check repository for a nested entity ) 
Tengo una entidad 99887776655443311 9988777665544332 . Dado la entidad Product3, mi aplicación verifica la base de datos a través de un repositorio para ve...

2  Modelo DDD de una compra en Nodejs  ( Ddd model of a purchase in nodejs ) 
Ya tengo alguna experiencia con Java y estoy acostumbrado a modelar con un enfoque DDD. Por lo tanto, ahora estoy comenzando con JS y Nodejs y me gustaría sab...

3  Conjuntos contables e incontables en .NET (IEnumerable y predicado)  ( Countable and uncountable sets in net ienumerable and predicate ) 
Hay un soporte completo completo de conjuntos contables en .NET: first, last8 . ¿Qué pasa con los conjuntos incontables? Conjuntos definidos por el predicado...

2  Representando una propiedad con un getter no nulable y un setter anulable  ( Representing a property with a non nullable getter a nullable setter ) 
Tengo una clase de la siguiente manera, donde quiero que el usuario de la clase sepa que configurable la propiedad de la babosa a null se admite , y su ge...

3  Clase de condición  ( Condition class ) 
versión actualizada : Conjuntos contables e incontables en .NET (IEnumerable y predicado) . Aquí está mi propia clase de predicado; Está equipado con alg...

1  Reemplazo de una clase de servicio de aplicación con múltiples manipuladores de comando  ( Replacing an application service class with multiple command handlers ) 
Tengo un controlador que utiliza un servicio de aplicación para lograr sus tareas. La clase de servicio está comenzando a cultivar grandes y desarrollando múl...

2  Conjuntos contables e incontables en .NET (versión limpia)  ( Countable and uncountable sets in net clean version ) 
Lo siento, solía ser una basura retirada en Conjuntos contables e incontables . Ahora y aquí está limpio. P.s. No me gustaría decir que esta cosa está a pu...

2  Conjuntos contables e incontables en .NET (Set Operators, Demo)  ( Countable and uncountable sets in net set operators demo ) 
versión actualizada : conjuntos iEnumerables y predicados en .NET (completo, explicado) . anteriormente: Conjuntos contables e incontables en .net (limpi...

2  Oop en el límite  ( Oop at the boundary ) 
Estoy tratando de implementar un servicio simple en la forma verdadera. Esto significa para mí que los objetos de dominio no tienen métodos técnicos o no rela...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos