Skip to content
This repository has been archived by the owner on Dec 12, 2022. It is now read-only.

Commit

Permalink
Merge branch 'entitydb-io:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
the-avid-engineer authored Jul 4, 2022
2 parents 2d782f0 + bdb32b4 commit 8b71c5b
Show file tree
Hide file tree
Showing 217 changed files with 4,073 additions and 2,751 deletions.
12 changes: 0 additions & 12 deletions .config/dotnet-tools.json

This file was deleted.

14 changes: 2 additions & 12 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
on:
push:
branches:
- '**'
- main
tags-ignore:
- '**'
paths:
Expand All @@ -11,16 +11,6 @@ on:
- 'EntityDb.sln'
- 'src/**'
- 'test/**'
pull_request:
branches:
- '**'
paths:
- '.github/workflows/build.yml'
- 'global.json'
- 'Directory.Build.props'
- 'EntityDb.sln'
- 'src/**'
- 'test/**'

name: Build

Expand Down Expand Up @@ -50,4 +40,4 @@ jobs:
uses: codacy/codacy-coverage-reporter-action@v1
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
coverage-reports: ./TestResults/**/*.xml
coverage-reports: ./TestResults/**/*.xml
54 changes: 0 additions & 54 deletions .github/workflows/create-release-draft.yml

This file was deleted.

83 changes: 83 additions & 0 deletions .github/workflows/publish-beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
on: pull_request

name: Publish to Beta

jobs:
milestone:
name: Try Parsing Milestone
runs-on: ubuntu-latest
outputs:
is_semantic_version: ${{ steps.parse.outputs.is_semantic_version }}
version: ${{ github.event.pull_request.milestone.title }}
steps:
- id: parse
env:
GITHUB_MILESTONE: ${{ github.event.pull_request.milestone.title }}
run: |
# This pattern comes from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
# However, Bash uses POSIX regular expressions, and there are some unsupported features.
# POSIX does not support non-capturing groups: (?:...)
# - To make it compatible, the non-capture modifiers have been removed
# POSIX does not support the digit metacharacter: \d
# - To make it compatible, the digit metacharacters have been replaced with [0-9]
semantic_version_pattern='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$'
if [[ ${GITHUB_MILESTONE} =~ $semantic_version_pattern ]]; then
echo ::set-output name=is_semantic_version::'true'
else
echo ::set-output name=is_semantic_version::'false'
fi
beta:
needs: milestone
if: ${{ needs.milestone.outputs.is_semantic_version == 'true' }}
name: Publish to Beta
runs-on: ubuntu-latest
environment: beta
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Start Redis
uses: supercharge/redis-github-action@1.2.0
with:
redis-version: 6.2.5
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.6.0
with:
mongodb-version: 5.0.3
mongodb-replica-set: entitydb
- name: Install .NET SDK
uses: actions/setup-dotnet@v1
- name: Restore Dependencies
run: dotnet restore EntityDb.sln --locked-mode
- name: Run Project Tests
run: dotnet test EntityDb.sln --no-restore -c Debug --collect:"XPlat Code Coverage" -r ./TestResults -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
- name: Pack Projects into Nuget Packages
run: dotnet pack EntityDb.sln --no-restore -c Release /p:Version='${{ needs.milestone.outputs.version }}-beta.${{ github.event.number }}.${{ github.run_number }}.${{ github.run_attempt }}'
- name: Publish to Beta
run: dotnet nuget push ./**/*.nupkg -s ${{ secrets.NUGET_SOURCE }} -k ${{ secrets.NUGET_API_KEY }}
- name: Packages & Symbols Artifact
uses: actions/upload-artifact@v2.2.3
with:
name: Packages & Symbols
path: |
./**/*.nupkg
./**/*.snupkg
- name: Publish Coverage Results to Codacy
uses: codacy/codacy-coverage-reporter-action@v1
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
coverage-reports: ./TestResults/**/*.xml
- name: Generate Coverage Report
uses: danielpalme/ReportGenerator-GitHub-Action@5.1.9
with:
reports: './TestResults/**/coverage.opencover.xml'
targetdir: 'CoverageReport'
reporttypes: 'HtmlInline'
license: ${{ secrets.REPORTGENERATOR_LICENSE }}
toolpath: 'reportgeneratortool'
- name: Coverage Report Artifact
uses: actions/upload-artifact@v2.2.3
with:
name: CoverageReport
path: CoverageReport
37 changes: 32 additions & 5 deletions .github/workflows/publish-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,42 @@ on:
name: Publish to Stable

jobs:
extract:
name: Extract Release Information
tag:
name: Try Parsing Tag
runs-on: ubuntu-latest
outputs:
is_semantic_version: ${{ steps.parse.outputs.is_semantic_version }}
version: ${{ steps.get_release.outputs.tag_name }}
steps:
- id: get_release
uses: bruceadams/get-release@v1.2.2
env:
GITHUB_TOKEN: ${{ github.token }}
- id: parse
env:
GITHUB_TAG: ${{ steps.get_release.outputs.tag_name }}
run: |
# This pattern comes from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
# However, Bash uses POSIX regular expressions, and there are some unsupported features.
# POSIX does not support non-capturing groups: (?:...)
# - To make it compatible, the non-capture modifiers have been removed
# POSIX does not support the digit metacharacter: \d
# - To make it compatible, the digit metacharacters have been replaced with [0-9]
semantic_version_pattern='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$'
if [[ ${GITHUB_TAG} =~ $semantic_version_pattern ]]; then
echo ::set-output name=is_semantic_version::'true'
else
echo ::set-output name=is_semantic_version::'false'
fi
stable:
needs: tag
if: ${{ needs.tag.outputs.is_semantic_version == 'true' }}
name: Publish to Stable
runs-on: ubuntu-latest
environment: stable
needs: extract
steps:
- name: Checkout Repository
uses: actions/checkout@v2
Expand All @@ -41,6 +61,13 @@ jobs:
- name: Run Project Tests
run: dotnet test EntityDb.sln --no-restore -c Debug
- name: Pack Projects into Nuget Packages
run: dotnet pack EntityDb.sln --no-restore -c Release /p:Version=${{ needs.extract.outputs.version }}
run: dotnet pack EntityDb.sln --no-restore -c Release /p:Version=${{ needs.tag.outputs.version }}
- name: Publish to Stable
run: dotnet nuget push */**.nupkg -s ${{ secrets.NUGET_SOURCE }} -k ${{ secrets.NUGET_API_KEY }}
run: dotnet nuget push ./**/*.nupkg -s ${{ secrets.NUGET_SOURCE }} -k ${{ secrets.NUGET_API_KEY }}
- name: Packages & Symbols Artifact
uses: actions/upload-artifact@v2.2.3
with:
name: Packages & Symbols
path: |
./**/*.nupkg
./**/*.snupkg
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<EnablePreviewFeatures>True</EnablePreviewFeatures>
</PropertyGroup>
</PropertyGroup>
</Project>
5 changes: 3 additions & 2 deletions EntityDb.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30105.148
# Visual Studio Version 17
VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ABACFBCC-B59F-4616-B6CC-99C37AEC8960}"
ProjectSection(SolutionItems) = preProject
Expand All @@ -25,6 +25,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{92484C44-2754-4C1D-BD46-98D83E4020EE}"
ProjectSection(SolutionItems) = preProject
test\Directory.Build.props = test\Directory.Build.props
test\docker-compose.yml = test\docker-compose.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Common.Tests", "test\EntityDb.Common.Tests\EntityDb.Common.Tests.csproj", "{CF316519-525E-4A67-BF12-1FDDF802B878}"
Expand Down
96 changes: 67 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,48 +25,86 @@ information on the statement. (If I'm wrong, you should consider getting a new b

## How does EntityDb.NET implement Event Sourcing?

There are five core objects at the heart of this implementation

1. Transactions
2. Agent
3. Commands
4. Tags
5. Leases
There are several core objects at the heart of this implementation. Encalsulating these objects are various repositories.

1. Transaction Repository
- Agents
- Commands
- Tags
- Leases
2. Snapshot Repository
- Snapshots
2. Entity Repository
- Transaction Repository
- Optional: Snapshot Repository
3. Projection Repository
- Transaction Repository
- Optional: Snapshot Repository

### Transactions

A transaction represents an atomic operation on multiple entities. A transaction is ideally* committed atomically or not at all.
If some step in the transaction fails, the entire transaction should fail.

*In the MongoDb implementation, the transaction _is_ committed atomically. However, it is possible in the future that there
will be implementations that are not capable of doing this if you want to use tags and/or leases. An example would be EventStore,
which provides no ability to enforce uniqueness constraints in its transaction. Such implementations will need a complimentary
transaction in order to make use of tags and leases.
A transaction represents an atomic operation on multiple entities. A transaction is committed atomically or not
at all. If some step in the transaction fails, the entire transaction should fail.

### Agent
### Agents

An agent is an actor that can execute transactions. For example, if a transaction is initiated via an HTTP API, you might use
the `HttpContextAgent` - it's signature includes headers and connection information, and it uses the ClaimsPrincipal to decide
if an agent has a particular role required for authorized commands.
An agent is an actor that can execute transactions. For example, if a transaction is initiated via an HTTP API, you
might use the `HttpContextAgent` - it's signature includes headers and connection information, and it uses the
ClaimsPrincipal to decide if an agent has a particular role required for authorized commands.

### Commands

A command represents the intent to perform some operation on a single entity. Going back to the bank account example, one
command could be `PerformDeposit` while another could be `PerformWithdrawl`. The things that you can do are commands.
A command represents the intent to perform some operation on a single entity. Going back to the bank account example,
one command could be `PerformDeposit` while another could be `PerformWithdrawl`. The things that you can do are
commands.

### Tags

A tag is a way to index entities by some piece of information. A tag can have a label and a value, both of which are strings.
Many accounts are typed, and you could represent this with a tag where `Label` is `Type` and `Value` is `Savings` or `Checking`.
You could then run a query to get the account id of all accounts where `Label` is `Type` and `Value` is `Savings`. The number
of savings accounts in the system would be the number of entity ids.
A tag is a way to index entities by some piece of information. A tag can have a label and a value, both of which are
strings. Many accounts are typed, and you could represent this with a tag where `Label` is `Type` and `Value`
is `Savings` or `Checking`. You could then run a query to get the account id of all accounts where `Label` is `Type`
and `Value` is `Savings`. The number of savings accounts in the system would be the number of entity ids.

### Leases

A lease is like a tag, except that it has a uniqueness constraint. Many banks have online portals, allowing bank members
to see their accounts on the internet. From the bank's perspective, all of the accounts should be tied to a member id,
probably a guid. But the member will not want to remember nor lookup this guid - they will want to use a username.
What you can do in EntityDb is make a lease for member entities where the entity id is the member id, the `Label` is `Username`
and the `Value` is whatever username the member wants to use. If an attempt to commit a transaction is made that would violate the
uniqueness constraint, it will be rejected. (This is obnoxious behavior for the user, though, so the bank should check before attempting
to commit to see if the username is available and give immediate feedback to choose a different username).
probably a guid. But the member will not want to remember nor lookup this guid - they will want to use a username. What
you can do in EntityDb is make a lease for member entities where the entity id is the member id, the `Label`
is `Username`
and the `Value` is whatever username the member wants to use. If an attempt to commit a transaction is made that would
violate the uniqueness constraint, it will be rejected. (This is obnoxious behavior for the user, though, so the bank
should check before attempting to commit to see if the username is available and give immediate feedback to choose a
different username).

### Snapshots

A snapshot is a stateful object at a given point in time. They always have an identifier and a version number.
Together, the identifier and version number called a pointer. You can request different versions of a given snapshot
by using different pointers!

In the context of snapshots, the reserved version number is reserved for pointing to the latest snapshot.
So if you want the latest version, you use a pointer with the exact id and the reserved version number.
If you want a specific version, you can create pointer with the exact id and version number you want.

The balance on your bank account is a snapshot. You can build that snapshot by summing all of the deposits and
withdrawls on your account. If you look at the bank statements, you will most likely see the snapshot of each bank
account for that statement, along with all of the deposits, withdrawls, and interest.

### Entities

An entity is conceptually an aggregate root inside of a bounded context, and it extends the concept of a snapshot.
In the banking example, there are multiple entities. You have a membership at the bank. That's an entity. You probably
have a checking account. That's an entity. And you might even have a savings account. That is also an entity!

Which bounded contexts these entiies live in is up to the business.

### Projections

A projection is an aggregate, but notably _not_ the aggregate root, and it too extends the concept of a snapshot.
In the banking example, one example of a projection could be your entire account balance. It can be anything, though!
You are not constrained in what data you want to use for your projection.

### Communications

- Coming Soon!<sup>TM</sup>
Loading

0 comments on commit 8b71c5b

Please sign in to comment.