Sharpino 1.2.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Sharpino --version 1.2.0                
NuGet\Install-Package Sharpino -Version 1.2.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sharpino" Version="1.2.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Sharpino --version 1.2.0                
#r "nuget: Sharpino, 1.2.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Sharpino as a Cake Addin
#addin nuget:?package=Sharpino&version=1.2.0

// Install Sharpino as a Cake Tool
#tool nuget:?package=Sharpino&version=1.2.0                

Sharpino

<img src="ico/sharpino.png" alt="drawing" width="50"/>

A little event-sourcing framework for F#

a library and an example of a simple event-sourcing framework in F#.

Projects:

Sharpino.Lib:

  • Core.fs: Abstract definition of Events, Commands and Undoer (the reverse of a command to be used if storage lacks transaction between streams). Definition of EvolveUnForgivingErrors and "normal" Evolve. The former rise an error if there are some events that cannot be applied to the current state of the aggregate. The latter just skip those events. Note: in some strict setups, for instance in using Postgres or in-memory storage, and in making the command be processed by the Mailboxprocessor (which is how you will see in the examples ensuring single-threaded execution of command->event->store), the EvolveUnForgivingErrors would not be necessary. However, it is theoretically possible to have inconsistent events when the order of the events is not guaranteed (e.g. in a distributed system). To increase the throughput you may actually want to process commands not in single-thread anymore assuming the risk of storing conflicting/inconsistent events.

  • Repository.fs: gets and stores snapshots, execute commands, produces and store events using the storage.

  • LightRepository.fs: gets and stores snapshots, execute commands, produces and store events using a storage that supports pub/sub model (only Eventstoredb at the moment).

  • DbStorage.fs and MemoryStorage.fs: Manages persistency in Postgres or in memory.

  • Cache.fs. Cache events, snapshots and state

Sharpino.Sample: You need a user called 'safe' with password 'safe' in your postgres (if you want to use postgres as event store).

It is an example of a library for managing todos with tags and categories. There are two versions in the sense of two different configurations concerning the distribution of the models (collection of entities) between the aggregates. There is a strategy to test the migration between versions (aggregate refactoring) that is described in the code (See: AppVersions.fs and MultiVersionsTests.fs) .

  • entities (e.g. Entities) manage entities.

  • aggregates (e.g. TodoAggregate) own a partition of the models and provide members to handle them.

  • aggregate members have corresponding events (e.g. TagsEvents) that are Discriminated Unions cases. Event types implement the Process interface.

  • aggregates are related to Commands (e.g. TagCommand) that are Discriminated Unions cases that can return lists of events by implementing the Executable interface. Commands defines also undoers that are functions that can undo the commands to reverse action in a multiple-stream operation for storage that don't support multiple-stream transactions (see LightRepository).

  • A Storage stores and retrieves aggregates, events and snapshots.

  • The Repository can build and retrieve snapshots, run the commands and store the related events.

  • The api layer functions provide business logic involving one or more aggregates by accessing their state, and by building one or more commands and sending them to the repository.

  • An example of how to handle multiple versions of the application to help refactoring and migration between different versions: application versions.

Sharpino.Sample.tests

  • tests for the sample application

How to use it

  • Just use ordinary dotnet command line tools for building the solution. Particularly you can run tests of the sample application by using the following command:
dotnet test 

You can also run the tests by the following command from Sharpino.Sample.Tests folder:

dotnet run

In the latter case, you get the output from Expecto test runner (in this case the console shows eventual standard output/printf).

By default, the tests run only the in-memory implementation of the storage. You can set up the postgres tables and db by using dbmate. In the Sharpino.Sample folder you can run the following command to setup the Postgres database:

dbmate -e DATABASE_URL up

(see the .env to set up the DATABASE_URL environment variable to connect to the Postgres database with a connection string). If you have Eventstore the standard configuration should work. (I have tested it with Eventstore 20.10.2-alpha on M2 Apple Silicon chip under Docker).

Tests on eventstoredb

If eventstore is running on docker you may want to run tests on it as follow: by making the eventstore tests not pending (search for "eventstore tests" and change ptestList to ftestList) or by uncommenting the following line on the file MultiversionsTests.fs:

        // (AppVersions.evSApp,                    AppVersions.evSApp,                 fun () -> () |> Result.Ok)

Warning: on testing eventstoredb you may experience some random test falures: I added some force update and some little delays to avoid, even though they can arise sometimes anyway in a non reproducible way. The cure is adding more delay if needed.

Faq:

  • Why "Sharpino"?
    • It's a mix of Sharp and fino (Italian for "thin"). "sciarpino" (same pronunciation) in Italian means also "little scarf".
  • Why another event-sourcing library?
    • I wanted to study the subject and it ended up in a tiny little framework.
  • Why F#?
    • Any functional language from the ML family language in my opinion is a good fit for the following reasons:
      • Events are immutable, building the state of the aggregates is a function of those events.
      • Discriminated Unions are suitable to represent events and commands.
      • The use of the lambda expression is a nice trick for the undoers (the under is returned as a lambda that retrieves the context for applying the undo and returns another lambda that actually can "undo" the command).
      • It is a .net language, so you can use everything in the .net ecosystem (including C# libraries).
  • How to use it
    • add the nuget package Sharpino to your project (current version 1.0.1)
    • note: on my side, when I added Sharpino as a library into a web app then I had to add the following line to the web app project file to avoid a false error (the error was "A function labeled with the 'EntryPointAttribute' attribute must be the last declaration")
    <GenerateProgramFile>false</GenerateProgramFile>
    

A small gitbook on the subject is available (click to read)

Product Compatible and additional computed target framework versions.
.NET Framework net is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.7.7 73 11/4/2024
2.7.6 71 11/3/2024
2.7.5 83 10/29/2024
2.7.4 77 10/25/2024
2.7.3 63 10/21/2024
2.7.2 71 10/21/2024
2.7.1 93 10/20/2024
2.7.0 81 10/20/2024
2.6.9 78 10/16/2024
2.6.8 86 10/16/2024
2.6.7 77 10/14/2024
2.6.6 68 10/14/2024
2.6.5 81 10/10/2024
2.6.4 75 10/8/2024
2.6.3 88 9/30/2024
2.6.2 87 9/28/2024
2.6.1 81 9/28/2024
2.6.0 85 9/24/2024
2.5.9 83 9/24/2024
2.5.8 91 9/18/2024
2.5.7 105 9/16/2024
2.5.6 118 9/14/2024
2.5.5 124 8/22/2024
2.5.4 127 8/15/2024
2.5.3 120 8/13/2024
2.5.2 113 8/9/2024
2.5.1 64 7/29/2024
2.5.0 107 7/19/2024
2.4.2 103 7/14/2024
2.4.1 105 7/13/2024
2.4.0 132 7/6/2024
2.3.0 116 6/28/2024
2.2.9 118 6/27/2024 2.2.9 is deprecated because it has critical bugs.
2.2.8 102 6/26/2024
2.2.7 105 6/23/2024
2.2.6 96 6/18/2024
2.2.5 96 6/17/2024
2.2.4 103 5/29/2024
2.2.3 112 5/25/2024
2.2.2 107 5/19/2024
2.2.1 102 5/14/2024
2.2.0 97 5/14/2024
2.1.3 91 5/12/2024
2.1.2 104 5/10/2024
2.1.1 116 5/9/2024
2.1.0 106 5/8/2024
2.0.7 117 5/6/2024
2.0.6 111 5/4/2024
2.0.5 74 5/3/2024
2.0.4 66 5/3/2024
2.0.3 65 5/3/2024
2.0.2 69 5/3/2024 2.0.2 is deprecated because it has critical bugs.
2.0.1 60 5/2/2024
2.0.0 72 5/2/2024
1.6.6 103 4/29/2024
1.6.5 115 4/28/2024
1.6.4 96 4/28/2024
1.6.3 113 4/25/2024
1.6.2 119 4/23/2024
1.6.1 109 4/21/2024
1.6.0 126 4/6/2024
1.5.9 173 3/17/2024
1.5.8 182 3/11/2024
1.5.7 167 3/10/2024
1.5.6 207 3/4/2024
1.5.5 211 3/3/2024
1.5.4 211 2/26/2024
1.5.3 256 2/25/2024 1.5.3 is deprecated because it has critical bugs.
1.5.2 217 2/18/2024
1.5.1 273 2/7/2024
1.5.0 247 2/1/2024
1.4.9 286 1/26/2024
1.4.8 285 1/25/2024
1.4.7 356 1/2/2024
1.4.6 375 12/19/2023
1.4.5 377 12/19/2023
1.4.4 409 12/14/2023
1.4.3 382 12/13/2023
1.4.1 437 12/9/2023
1.4.0 412 12/8/2023
1.3.9 453 12/1/2023
1.3.8 479 11/29/2023
1.3.6 437 11/29/2023
1.3.4 436 11/27/2023
1.3.3 448 11/27/2023
1.3.2 449 11/16/2023
1.3.1 419 11/15/2023
1.3.0 432 11/9/2023
1.3.0-beta 392 11/9/2023
1.2.8 421 11/6/2023
1.2.7 433 11/5/2023
1.2.6 462 11/3/2023
1.2.5 456 11/2/2023
1.2.4 478 10/21/2023
1.2.3 499 10/14/2023
1.2.2 444 10/13/2023
1.2.0 467 10/13/2023
1.1.0 479 10/7/2023
1.0.2 464 9/20/2023
1.0.1 497 9/12/2023
1.0.0 1,665 9/12/2023