Skip to content

Repository files navigation

Sample

Hey, it's Georgii here 👋

Meet an enterprise software developer faced a lot of mature systems
successfully competing in the market. Sounds too good? 😏 Unless
bringing numbers of greenfield projects you'd surely face consequences
of that "success" 🙈 When it comes to "what took you so long?".

Here is a half decade pursued and finally embodied solution to the
problem of degrading development performance. It's tied up with
canonical production ready approaches for typical enterprise software
components. You'll find reasoning to your own framework ownerhip at a
complexity level you decide.

⚠️ WIP warning ⚠️

There's lack of some modules and examples that are to be implemented
in the near future. Hopefully the published work will hint you what to
expect next and decide whether to go deeper. Here are some TODOs:

1. Example per commit ... covering implemented modules
2. Modular split table YourCompany.OLTP.RecordsManagement.DI.TransactionalComposition.EFCore
3. Events with native broker client YourCompany.OLTP.StateOwnership.EventsProducing.RabbitMQ
4. The outbox YourCompany.OLTP.StateOwnership.EventsProducing.RabbitMQ.EFCore
5. Events processing YourCompany.OLTP.RecordsManagement.DI.RabbitMQ
6. Cross-cutting processing YourCompany.OLTP.RecordsManagement.DI.TransactionalComposition.RabbitMQ
7. Auto-generated full fledged REST API YourCompany.OLTP.RecordsManagement.AspNet
8. To be continued ... other reusable modules

Goals

  1. Comprehensive educational material for beginners (or your "Agents")
    focused on bringing "human-understandable" business value rather than
    the-faster-and-more-code-the-better.
  2. Exhaustive open-source boilerplate covering all typical needs of
    enterprise development properly put together for easy adoption to your
    ownership at the entry point of choice.
  3. Shift the industry and protect the mass of projects from mistakes
    leading 9 of 10 to unmaintainable state after 3 years developming
    in Deadline-Driven-Development environment (DDD 🤣🤣🤣)...
    no matter architectural choices of its time.
  4. Bring personally gained experience in a form that is too complex to
    achieve while in rush on those 9 of 10.

Metrics

Measure-Object -Line

The most important one 🤔 As practice shows, the amount is the main
performance killer 💀 (esp. by duplicating and glue-code bloating 💩).

The rule is The less code doing the job, the more value it brings.
Each piece you find here is evaluated with this metric.

Get-ChildItem -Exclude examples |
    Get-ChildItem -Recurse -Filter '*.cs' -File |
    Where-Object { $_.FullName -match '^(?:(?!examples\\).)*src\\YourCompany(?:(?!\\obj\\|\\bin\\).)+$' } |
    ForEach-Object {
        $measured = [System.IO.File]::ReadAllText($_.FullName, [System.Text.Encoding]::UTF8) | Measure-Object -Line;
        [PSCustomObject]@{ Folder = $_.FullName; LineCount = $measured.Lines };
    } |
    Measure-Object -Property LineCount -Sum

Wouldn't wish anyone an AI to assist one reaching the unmaintainable
human-incomprehensible amount of code as fast as six months rather
in those 3 years originally.

Project structure

Branching rules

The main branch is "public" with commits of two types in its "trunk":

  • commit --fixup
    these are used whenever any change is intended for the latest version.
    The fixups are always on top of the whole "entry point" branch graph
    where more complex entry points include less complex ending up into
    a holistic single solution (ie. the single merge commit into public).

    at least one of the fixups must change the root commit for
    proper visualiation of the rebase resulting graph.

  • rebase -i --autosquash --rebase-merges --update-refs
    following fixup rules this command copies the whole solution
    while retaining the entry point branches structure. The resulting
    graph's top commit always remain "merged to the trunk".

This way you'll never lose the entry point you had chosen when
cloned or submoduled this repo. And with versioning rules below
all the versions stay visualized with all the past fixups applied.
In other words the structure is consistent and immutable.

If used in your project code this approach acts as an extension to
Trunk Based Development.
Precisely the trunk is formed up with the fixups only while the roots
are updated at the end of iteration making your git history to be
self-documented and clean. Find more in ChatGPT generated article:
Two‑level rebase architectural narrative and evolution of the OLTP model.

⚠️ WIP warning ⚠️

From time to time you may fetch specially prefixed wip- branches.
Those aren't entry point branches yet so they won't be as safe and
consistent to use as the already finished ones (reachable from public).

Please be careful relying on them but the same time don't miss the
opportunity to look a little further and meet those coming soon
modules covering the extended needs you may demanded so long or
couldn't even imagine!

Documenting

The main form of documenting is commit bodyes' description.
The commits within an entry point branch may be additionally
split by different authors. Authors are your teammates:

  • 🏠 Domainman - demands tools to speak business over the code
    while delegating its implementation. Forms up the framework you own.

    One remains an expert regardless the stack or even the
    programming language and develops domain modules.

  • 🧱 Facademan - puts the domain modules together with tools like
    ASP.NET, OpenAPI, Configuration, Reflection, Serialization, DI.
    Exposes APIs, forms up supported API client stacks.

    One possesses best knowledge of the chosen entry point and
    organizes its further development.

  • 💾 Persistman - speaks database over the code (not the one
    restoring from backups, wish you'd never know it 😅).
    Irreplaceble when it comes to EFCore or other persistence tools.

    One adopts, optimizes, upgrades database accessing modules
    (unless being constantly distracted by Domainman 🙈).

  • 📦 Streamman - let it be either a broker's native client
    library or a complex proxying framework like MassTransit or just
    bare queue-table outbox - will depend on Domainman needs.

    One organizes data delivery and analysis, from
    cross-cutting concerns, logging, short-circuiting, scheduling
    to implementing specific data analysing domain services.

Likewise, each entry point's example project is accompanied with
README.md referenced in Entry points section below.

ℹ️ Studying advise

Make use of the editor's per-line git blaming abilities from
extensions like eamodio.gitlens. This helps a lot to grasp reasoning
behind an arbitrary line you're curently looking at at the right moment.

🎁 Bonus

You may find interesting to look at the last rebase todo list.

It is divided into Startup Iterations where your teammates work
together to build up the framework and then switch into their
dedicated fields. Btw, exactly those iteration numbers you can
see in the very beginning of the authored commits (1|, 2|, 3| ...).

Btw, updating this one for each version helps following the
branching rule of fixups to always contain one for the root commit 😉.

Versioning

This is a boilerplate repo with consistent solution structure where
each subsequent fixup serie results in a new version of the
"entry point" branch graph.

In addition to current version's entry point branches there are
similarly named version-numbered tags. You can find it useful for
documenting purposes, using them for stable links in your documenation
letting to navigate to and browse your entry point online.

Though only the current version's "public-N" tag can be moved, once
another approved fixups' PR is rebased or squashed into the "public".

This way the boilerplate becomes safe for using in any form, from
simple submoduling to advanced hosting in your enterprise repo.

Motivation of this strategy is simple:

If you treat your "business-logic" valuable - you're interested in
owning the framework rather than "renting" it evenually.

Current solution

This is a graph of the current entry point's solution aligned with
the Startup Iterations (more in the last rebase todo list).

The top most commit is the most recent one tagged in the graph with
the last entry point branch included by your current revision.
All the entry point branches' heads are highlighted in the graph as ⏹️.

See the detailed commit descriptions gathered in Commit logs section.

---
config:
  gitGraph:
    mainBranchName: "7|🧱: OLTP DI EFCore integrated"
    parallelCommits: true
    rotateCommitLabel: false
    showCommitLabel: false
---
gitGraph

%% commit type:HIGHLIGHT tag:"N|🧱|wip-full-solution" %% OEMEC
%% branch "N|🧱: OLTP EFCore multi-entity chassis" %% OEMEC
%% commit type:HIGHLIGHT tag:"N|🧱|wip-oltp-di-efcore-handle-specifications-only-the-rest-is-covered-with-record-types-composition|YourCompany.Configuration.EFCore" %% OEMEC

%% Startup Iteration 7

%% branch "7|💾: EFCore multi EntityEntry visitors" %% EMEEV
%% commit type:NORMAL tag:"7|💾|YourCompany.Configuration.EFCore" %% EMEEV

%% checkout "N|🧱: OLTP EFCore multi-entity chassis" %% EMES
%% branch "7|💾: EFCore multi-entity sorting" %% EMES
%% commit type:NORMAL tag:"7|💾|YourCompany.Configuration.EFCore" %% EMES

%% checkout "7|💾: EFCore multi-entity sorting" %% ESME
%% branch "7|💾: EFCore SortingKey multi-entity" %% ESME
%% merge "7|💾: EFCore multi EntityEntry visitors" type:NORMAL tag:"7|💾|YourCompany.Configuration.EFCore" %% ESME

%% checkout "N|🧱: OLTP EFCore multi-entity chassis" %% ODEI
%% branch "7|🧱: OLTP DI EFCore integrated" %% ODEI
%% commit type:REVERSE %% ODEI>1
%% commit type:REVERSE %% ODEI>2
commit type:HIGHLIGHT tag:"7|🧱|public|oltp-di-efcore-handle-specifications-only-the-rest-is-covered|YourCompany.Configuration.EFCore|YourCompany.OLTP.RecordsManagement.DI.EFCore|YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore" %% ODEI

branch "7|🧱: OLTP DI EFCore switch to chassis" %% ODESTC
%% commit type:HIGHLIGHT tag:"7|🧱|oltp-di-efcore-handle-specifications-only-the-rest-is-covered|YourCompany.Configuration.EFCore|YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore" %% ODESTC
commit type:NORMAL tag:"7|🧱|YourCompany.Configuration.EFCore|YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore" %% ODESTC

%% Startup Iteration 6

checkout "7|🧱: OLTP DI EFCore switch to chassis" %% OEF
branch "6|💾: OLTP EFCore finalized" %% OEF
commit type:NORMAL tag:"6|💾|YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore" %% OEF

%% Startup Iteration 5

checkout "6|💾: OLTP EFCore finalized" %% OER
branch "5|💾: OLTP EFCore readonly" %% OER
%% commit type:REVERSE %% OER>1
%% commit type:REVERSE %% OER>2
%% commit type:REVERSE %% OER>3
%% commit type:REVERSE %% OER>4
%% commit type:REVERSE %% OER>5
commit type:NORMAL tag:"5|💾|YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore" %% OER

%% checkout "7|🧱: OLTP DI EFCore integrated" %% ODMR
%% checkout "N|🧱: OLTP EFCore multi-entity chassis" %% ODMR
%% branch "5|🧱: OLTP DI mixed repository" %% ODMR
%% commit type:REVERSE %% ODMR>1
%% commit type:REVERSE %% ODMR>2
%% commit type:REVERSE %% ODMR>3
%% commit type:REVERSE %% ODMR>4
%% commit type:REVERSE %% ODMR>5
%% commit type:HIGHLIGHT tag:"5|🧱|oltp-di-combine-use-case-and-persistence-interfaces-with-record-types-composition|YourCompany.OLTP.RecordsManagement.DI.TransactionalComposition" %% ODMR

%% branch "5|🧱: OLTP record composition to chassis" %% ORCTC
%% commit type:HIGHLIGHT tag:"5|🧱|oltp-di-combine-use-case-and-persistence-interfaces-with-record-types-composition|YourCompany.OLTP.RecordsManagement.DI.TransactionalComposition|YourCompany.OLTP.StateOwnership.TransactionalComposition" %% ORCTC
%% commit type:NORMAL tag:"5|🧱|YourCompany.OLTP.RecordsManagement.DI.TransactionalComposition|YourCompany.OLTP.StateOwnership.TransactionalComposition" %% ORCTC

checkout "7|🧱: OLTP DI EFCore switch to chassis" %% ODIFR
branch "5|🧱: OLTP DI inheritance free repo" %% ODIFR
commit type:REVERSE %% ODIFR>1
commit type:HIGHLIGHT tag:"5|🧱|oltp-di-combine-use-case-and-persistence-interfaces-without-inheritance|YourCompany.OLTP.RecordsManagement.DI" %% ODIFR
%% merge "5|🧱: OLTP record composition to chassis" type:HIGHLIGHT tag:"5|🧱|oltp-di-combine-use-case-and-persistence-interfaces-without-inheritance|YourCompany.OLTP.RecordsManagement.DI" %% ODIFR

branch "5|🧱: OLTP DI ScopedUseCasesProvider" %% ODS
commit type:NORMAL tag:"5|🧱|YourCompany.OLTP.RecordsManagement.UseCases.Reflection.DI" %% 5|🧱: OLTP DI ScopedUseCasesProvider

checkout "5|🧱: OLTP DI inheritance free repo" %% ODSRP
branch "5|🧱: OLTP DI ScopedRecordsProvider" %% ODSRP
commit type:NORMAL tag:"5|🧱|YourCompany.OLTP.StateOwnership.Reflection.DI" %% ODSRP

checkout "5|🧱: OLTP DI inheritance free repo" %% OSTC
%% commit type:REVERSE %% OSTC<1
%% commit type:REVERSE %% OSTC<2
%% commit type:REVERSE %% OSTC<3
branch "5|🧱: OLTP switch to chassis" %% OSTC
commit type:REVERSE %% OSTC>1
commit type:HIGHLIGHT tag:"5|🧱|oltp-di-combine-use-case-and-persistence-interfaces-switch-to-chassis|YourCompany.OLTP.RecordsManagement|YourCompany.OLTP.RecordsManagement.DI|YourCompany.OLTP.RecordsManagement.Persistence|YourCompany.OLTP.RecordsManagement.UseCases|YourCompany.OLTP.StateOwnership" %% OSTC

%% Startup Iteration 4

%% checkout "7|💾: EFCore multi-entity sorting" %% CEPFVT
%% branch "4|💾: cache EFProperty from ValueTuple" %% CEPFVT
%% commit type:REVERSE %% CEPFVT>1
%% commit type:REVERSE %% CEPFVT>2
%% commit type:REVERSE %% CEPFVT>3
%% commit type:REVERSE %% CEPFVT>4
%% commit type:REVERSE %% CEPFVT>5
%% commit type:REVERSE %% CEPFVT>6
%% commit type:REVERSE %% CEPFVT>7
%% commit type:REVERSE %% CEPFVT>8
%% commit type:REVERSE %% CEPFVT>9
%% commit type:REVERSE %% CEPFVT>10
%% commit type:REVERSE %% CEPFVT>11
%% commit type:NORMAL tag:"4|💾|YourCompany.Configuration.EFCore" %% CEPFVT

checkout "6|💾: OLTP EFCore finalized" %% EETSKT
branch "4|💾: EFEntityTypeSortingKeyTopology" %% EETSKT
commit type:REVERSE %% EETSKT>1
commit type:REVERSE %% EETSKT>2
commit type:REVERSE %% EETSKT>3
commit type:REVERSE %% EETSKT>4
commit type:REVERSE %% EETSKT>5
%% commit type:REVERSE %% EETSKT>6
%% commit type:REVERSE %% EETSKT>7
commit type:NORMAL tag:"4|💾|YourCompany.Configuration.EFCore" %% EETSKT

checkout "5|💾: OLTP EFCore readonly" %% Startup Iteration 4
commit type:REVERSE %% OER 4>1
commit type:REVERSE %% OER 4>2
commit type:REVERSE %% OER 4>3

checkout "5|💾: OLTP EFCore readonly" %% OEIG
branch "4|💾: OLTP EFCore ID generation" %% OEIG
commit type:REVERSE %% OEIG>1
commit type:NORMAL tag:"4|💾|YourCompany.OLTP.StateOwnership.Reflection.EFCore" %% OEIG

branch "4|💾: OLTP EFCore MetadataHelper" %% OEMH
commit type:NORMAL tag:"4|💾|YourCompany.OLTP.StateOwnership.Reflection.EFCore" %% OEMH

%% checkout "7|💾: EFCore multi EntityEntry visitors" %% Startup Iteration 4
%% commit type:REVERSE %% EMEEV 4>1
%% commit type:REVERSE %% EMEEV 4>2
%% commit type:REVERSE %% EMEEV 4>3
%% commit type:REVERSE %% EMEEV 4>4
%% commit type:REVERSE %% EMEEV 4>5
%% commit type:REVERSE %% EMEEV 4>6
%% commit type:REVERSE %% EMEEV 4>7
%% commit type:REVERSE %% EMEEV 4>8
%% commit type:REVERSE %% EMEEV 4>9
%% commit type:REVERSE %% EMEEV 4>10
%% commit type:REVERSE %% EMEEV 4>11
%% commit type:REVERSE %% EMEEV 4>12
%% commit type:REVERSE %% EMEEV 4>13

checkout "5|💾: OLTP EFCore readonly" %% ESKEE
branch "4|💾: EFCore SortingKey ⇆ EntityEntry" %% ESKEE
commit type:REVERSE %% ESKEE>1
commit type:REVERSE %% ESKEE>2
commit type:REVERSE %% ESKEE>3
commit type:NORMAL tag:"4|💾|YourCompany.Configuration.EFCore" %% ESKEE
%% merge "7|💾: EFCore multi EntityEntry visitors" type:NORMAL tag:"4|💾|YourCompany.Configuration.EFCore" %% ESKEE

branch "4|💾: EFEntityEntryPropertiesCache" %% EEEPC
commit type:NORMAL tag:"4|💾|YourCompany.Configuration.EFCore" %% EEEPC

%% checkout "7|💾: EFCore multi-entity sorting" %% Startup Iteration 4
%% commit type:REVERSE %% EMES 4>1
%% commit type:REVERSE %% EMES 4>2
%% commit type:REVERSE %% EMES 4>3
%% commit type:REVERSE %% EMES 4>4
%% commit type:REVERSE %% EMES 4>5
%% commit type:REVERSE %% EMES 4>6
%% commit type:REVERSE %% EMES 4>7
%% commit type:REVERSE %% EMES 4>8
%% commit type:REVERSE %% EMES 4>9
%% commit type:REVERSE %% EMES 4>10
%% commit type:REVERSE %% EMES 4>11
%% commit type:REVERSE %% EMES 4>12
%% commit type:REVERSE %% EMES 4>13
%% commit type:REVERSE %% EMES 4>14
%% commit type:REVERSE %% EMES 4>15
%% commit type:REVERSE %% EMES 4>16

checkout "5|💾: OLTP EFCore readonly" %% ESKR
branch "4|💾: EFCore SortingKey reading" %% ESKR
commit type:REVERSE %% ESKR>1
commit type:REVERSE %% ESKR>2
commit type:REVERSE %% ESKR>3
commit type:REVERSE %% ESKR>4
commit type:REVERSE %% ESKR>5
commit type:NORMAL tag:"4|💾|YourCompany.Configuration.EFCore" %% ESKR
%% merge "7|💾: EFCore multi-entity sorting" type:NORMAL tag:"4|💾|YourCompany.Configuration.EFCore" %% ESKR

checkout "5|🧱: OLTP switch to chassis" %% OLMBU
branch "4|🧱: OLTP LINQ may be useful" %% OLMBU
%% commit type:HIGHLIGHT tag:"4|🧱|oltp-segregate-persistence-repository-is-for-use-cases|YourCompany.OLTP.RecordsManagement.Persistence.Linq" %% OLMBU
merge "5|💾: OLTP EFCore readonly" type:HIGHLIGHT tag:"4|🧱|oltp-segregate-persistence-repository-is-for-use-cases|YourCompany.OLTP.RecordsManagement.Persistence.Linq" %% OLMBU

branch "4|🧱: OLTP identities are unique keys" %% OIAUK
%% commit type:REVERSE %% OIAUK>1
%% commit type:HIGHLIGHT tag:"4|🧱|oltp-segregate-persistence-repository-is-for-use-cases|YourCompany.OLTP.RecordsManagement.Persistence" %% OIAUK
commit type:NORMAL tag:"4|🧱|YourCompany.OLTP.RecordsManagement.Persistence" %% OIAUK

%% checkout "5|🧱: OLTP record composition to chassis" %% OUCBRT
%% branch "4|🏠: OLTP use cases by record type" %% OUCBRT
%% commit type:REVERSE %% OUCBRT>1
%% commit type:REVERSE %% OUCBRT>2
%% commit type:REVERSE %% OUCBRT>3
%% commit type:REVERSE %% OUCBRT>4
%% commit type:REVERSE %% OUCBRT>5
%% commit type:HIGHLIGHT tag:"4|🏠|oltp-segregate-use-cases-with-transactional-composition|YourCompany.OLTP.RecordsManagement.UseCases.TransactionalComposition" %% OUCBRT

%% checkout "4|🏠: OLTP use cases by record type" %% EOUC
checkout "5|🧱: OLTP switch to chassis" %% EOUC
branch "4|🏠: enumerate OLTP use cases" %% EOUC
commit type:HIGHLIGHT tag:"4|🏠|oltp-segregate-use-cases-repository-is-for-persistence|YourCompany.OLTP.RecordsManagement.UseCases" %% EOUC
%% merge "4|🏠: OLTP use cases by record type" type:HIGHLIGHT tag:"4|🏠|oltp-segregate-use-cases-repository-is-for-persistence|YourCompany.OLTP.RecordsManagement.UseCases" %% EOUC

%% Startup Iteration 3

checkout "4|💾: EFEntityTypeSortingKeyTopology" %% Startup Iteration 3
commit type:REVERSE %% EETSKT 3>1
commit type:REVERSE %% EETSKT 3>2
commit type:REVERSE %% EETSKT 3>3
commit type:REVERSE %% EETSKT 3>4
commit type:REVERSE %% EETSKT 3>5
%% commit type:REVERSE %% EETSKT 3>6

%% checkout "4|💾: EFCore SortingKey ⇆ EntityEntry" %% Startup Iteration 3
%% commit type:REVERSE %% ESKEE 3>1

checkout "4|💾: EFCore SortingKey ⇆ EntityEntry" %% EFCSK
%% checkout "7|💾: EFCore SortingKey multi-entity" %% EFCSK
branch "3|💾: EFCore first-class SortingKey" %% EFCSK
%% merge "4|💾: EFCore SortingKey ⇆ EntityEntry" %% EFCSK
merge "4|💾: EFCore SortingKey reading" %% EFCSK
%% merge "4|💾: EFCore SortingKey ⇆ EntityEntry" %% EFCSK
merge "4|💾: EFEntityTypeSortingKeyTopology" %% EFCSK
commit type:REVERSE %% EFCSK>1
%% commit type:REVERSE %% EFCSK>2
%% commit type:REVERSE %% EFCSK>3
%% commit type:REVERSE %% EFCSK>4
commit type:NORMAL tag:"3|💾|YourCompany.Configuration.EFCore|YourCompany.Configuration.EFCore.PostgreSQL|YourCompany.Configuration.EFCore.Sqlite" %% EFCSK

%% checkout "4|💾: cache EFProperty from ValueTuple" %% Startup Iteration 3
%% commit type:REVERSE %% CEPFVT 3>1
%% commit type:REVERSE %% CEPFVT 3>2
%% commit type:REVERSE %% CEPFVT 3>3
%% commit type:REVERSE %% CEPFVT 3>4
%% commit type:REVERSE %% CEPFVT 3>5
%% commit type:REVERSE %% CEPFVT 3>6
%% commit type:REVERSE %% CEPFVT 3>7

%% checkout "3|💾: EFCore first-class SortingKey" %% EPEC
branch "3|💾: EFPropertyExpressionsCache" %% EPEC
commit type:NORMAL tag:"3|💾|YourCompany.Configuration.EFCore" %% EPEC
%% merge "4|💾: cache EFProperty from ValueTuple" type:NORMAL tag:"3|💾|YourCompany.Configuration.EFCore" %% EPEC

%% checkout "4|🧱: OLTP identities are unique keys" %% Startup Iteration 3
%% commit type:REVERSE %% OIAUK 3>1

checkout "4|🏠: enumerate OLTP use cases" %% OTS
%% checkout "4|🧱: OLTP identities are unique keys" %% OTS
branch "3|🧱: OLTP transaction structure" %% OTS
merge "4|🧱: OLTP identities are unique keys" %% OTS
commit type:REVERSE %% OTS>1
commit type:REVERSE %% OTS>2
commit type:REVERSE %% OTS>3
commit type:REVERSE %% OTS>4
commit type:REVERSE %% OTS>5
commit type:REVERSE %% OTS>6
%% commit type:REVERSE %% OTS>7
commit type:HIGHLIGHT tag:"3|🧱|oltp-typical-transaction-structure-repository-is-up-to-you|YourCompany.OLTP.RecordsManagement" %% OTS
%% merge "4|🧱: OLTP identities are unique keys" type:HIGHLIGHT tag:"3|🧱|oltp-typical-transaction-structure-repository-is-up-to-you|YourCompany.OLTP.RecordsManagement" %% OTS

%% checkout "4|🏠: OLTP use cases by record type" %% ORTCM
%% branch "3|🏠: OLTP RecordTypesCompositionMap" %% ORTCM
%% commit type:REVERSE %% ORTCM>1
%% commit type:REVERSE %% ORTCM>2
%% commit type:REVERSE %% ORTCM>3
%% commit type:REVERSE %% ORTCM>4
%% commit type:REVERSE %% ORTCM>5
%% commit type:REVERSE %% ORTCM>6
%% commit type:REVERSE %% ORTCM>7
%% commit type:REVERSE %% ORTCM>8
%% commit type:REVERSE %% ORTCM>9
%% commit type:REVERSE %% ORTCM>10
%% commit type:NORMAL tag:"3|🏠|YourCompany.OLTP.StateOwnership.TransactionalComposition.Reflection" %% ORTCM

checkout "5|🧱: OLTP DI ScopedUseCasesProvider" %% OUCTM
commit type:REVERSE %% OUCTM<1
commit type:REVERSE %% OUCTM<2
commit type:REVERSE %% OUCTM<3
commit type:REVERSE %% OUCTM<4
commit type:REVERSE %% OUCTM<5
commit type:REVERSE %% OUCTM<6
commit type:REVERSE %% OUCTM<7
commit type:REVERSE %% OUCTM<8
commit type:REVERSE %% OUCTM<9
commit type:REVERSE %% OUCTM<10
commit type:REVERSE %% OUCTM<11
%% commit type:REVERSE %% OUCTM<12
%% commit type:REVERSE %% OUCTM<13
%% commit type:REVERSE %% OUCTM<14
%% commit type:REVERSE %% OUCTM<15
branch "3|🏠: OLTP UseCaseTypesMap" %% OUCTM
%% commit type:REVERSE %% OUCTM>1
%% commit type:REVERSE %% OUCTM>2
%% commit type:REVERSE %% OUCTM>3
%% commit type:REVERSE %% OUCTM>4
%% commit type:REVERSE %% OUCTM>5
%% commit type:REVERSE %% OUCTM>6
%% commit type:REVERSE %% OUCTM>7
commit type:NORMAL tag:"3|🏠|YourCompany.OLTP.RecordsManagement.UseCases.Reflection" %% OUCTM

checkout "4|🏠: enumerate OLTP use cases" %% GOUC
branch "3|🏠: generic OLTP use cases" %% GOUC
%% commit type:REVERSE %% GOUC>1
%% commit type:NORMAL tag:"3|🏠|YourCompany.OLTP.RecordsManagement.UseCases" %% GOUC
merge "3|🏠: OLTP UseCaseTypesMap" type:NORMAL tag:"3|🏠|YourCompany.OLTP.RecordsManagement.UseCases" %% GOUC

checkout "4|💾: OLTP EFCore MetadataHelper" %% Startup Iteration 3
commit type:REVERSE %% OEMH 3>1
commit type:REVERSE %% OEMH 3>2
commit type:REVERSE %% OEMH 3>3
commit type:REVERSE %% OEMH 3>4
commit type:REVERSE %% OEMH 3>5
commit type:REVERSE %% OEMH 3>6
commit type:REVERSE %% OEMH 3>7
commit type:REVERSE %% OEMH 3>8
commit type:REVERSE %% OEMH 3>9
commit type:REVERSE %% OEMH 3>10
%% commit type:REVERSE %% OEMH 3>11
%% commit type:REVERSE %% OEMH 3>12
%% commit type:REVERSE %% OEMH 3>13
%% commit type:REVERSE %% OEMH 3>14

%% checkout "3|🏠: OLTP RecordTypesCompositionMap" %% ORTM
checkout "5|🧱: OLTP DI ScopedRecordsProvider" %% ORTM
branch "3|🏠: OLTP RecordTypesMap" %% ORTM
%% merge "3|🏠: OLTP RecordTypesCompositionMap" %% ORTM
%% commit type:REVERSE %% ORTM>1
%% commit type:REVERSE %% ORTM>2
%% commit type:REVERSE %% ORTM>3
%% commit type:REVERSE %% ORTM>4
%% commit type:REVERSE %% ORTM>5
%% commit type:NORMAL tag:"3|🏠|YourCompany.OLTP.StateOwnership.Reflection" %% ORTM
%% merge "3|🏠: OLTP RecordTypesCompositionMap" type:NORMAL tag:"3|🏠|YourCompany.OLTP.StateOwnership.Reflection" %% ORTM
merge "4|💾: OLTP EFCore MetadataHelper" type:NORMAL tag:"3|🏠|YourCompany.OLTP.StateOwnership.Reflection" %% ORTM

%% Startup Iteration 2

%% checkout "N|🧱: wip switch to chassis" %% EHMR
%% branch "2|💾: EFCore hosting migration run" %% EHMR
%% commit type:REVERSE %% EHMR>1
%% commit type:REVERSE %% EHMR>2
%% commit type:REVERSE %% EHMR>3
%% commit type:REVERSE %% EHMR>4
%% commit type:REVERSE %% EHMR>5
%% commit type:REVERSE %% EHMR>6
%% commit type:REVERSE %% EHMR>7
%% commit type:REVERSE %% EHMR>8
%% commit type:REVERSE %% EHMR>9
%% commit type:REVERSE %% EHMR>10
%% commit type:REVERSE %% EHMR>11
%% commit type:REVERSE %% EHMR>12
%% commit type:REVERSE %% EHMR>13
%% commit type:REVERSE %% EHMR>14
%% commit type:REVERSE %% EHMR>15
%% commit type:REVERSE %% EHMR>16
%% commit type:REVERSE %% EHMR>17
%% commit type:REVERSE %% EHMR>18
%% commit type:REVERSE %% EHMR>19
%% commit type:REVERSE %% EHMR>20
%% commit type:REVERSE %% EHMR>21
%% commit type:REVERSE %% EHMR>22
%% commit type:REVERSE %% EHMR>23
%% commit type:REVERSE %% EHMR>24
%% commit type:REVERSE %% EHMR>25
%% commit type:REVERSE %% EHMR>26
%% commit type:REVERSE %% EHMR>27
%% commit type:REVERSE %% EHMR>28
%% commit type:REVERSE %% EHMR>29
%% commit type:REVERSE %% EHMR>30
%% commit type:REVERSE %% EHMR>31
%% commit type:REVERSE %% EHMR>32
%% commit type:REVERSE %% EHMR>33
%% commit type:NORMAL tag:"2|💾|YourCompany.Configuration.EFCore.Hosting" %% EHMR

checkout "6|💾: OLTP EFCore finalized" %% ECTTGS
commit type:REVERSE %% ECTTGS<1
commit type:REVERSE %% ECTTGS<2
commit type:REVERSE %% ECTTGS<3
commit type:REVERSE %% ECTTGS<4
commit type:REVERSE %% ECTTGS<5
commit type:REVERSE %% ECTTGS<6
commit type:REVERSE %% ECTTGS<7
commit type:REVERSE %% ECTTGS<8
commit type:REVERSE %% ECTTGS<9
commit type:REVERSE %% ECTTGS<10
commit type:REVERSE %% ECTTGS<11
commit type:REVERSE %% ECTTGS<12
commit type:REVERSE %% ECTTGS<13
commit type:REVERSE %% ECTTGS<14
commit type:REVERSE %% ECTTGS<15
commit type:REVERSE %% ECTTGS<16
commit type:REVERSE %% ECTTGS<17
commit type:REVERSE %% ECTTGS<18
commit type:REVERSE %% ECTTGS<19
%% commit type:REVERSE %% ECTTGS<20
%% commit type:REVERSE %% ECTTGS<21
%% commit type:REVERSE %% ECTTGS<22
%% commit type:REVERSE %% ECTTGS<23
%% commit type:REVERSE %% ECTTGS<24
%% commit type:REVERSE %% ECTTGS<25
branch "2|💾: EFChangeTrackerTrackGraphStrategy" %% ECTTGS
commit type:NORMAL tag:"2|💾|YourCompany.Configuration.EFCore" %% ECTTGS

checkout "6|💾: OLTP EFCore finalized" %% EPL
branch "2|💾: EFCore pessimistic locking" %% EPL
commit type:NORMAL tag:"2|💾|YourCompany.Configuration.EFCore|YourCompany.Configuration.EFCore.PostgreSQL|YourCompany.Configuration.EFCore.Sqlite" %% EPL

checkout "4|💾: EFEntityEntryPropertiesCache" %% BPE
branch "2|💾: basic pluggable EFCore" %% BPE

checkout "2|💾: EFChangeTrackerTrackGraphStrategy" %% BPE
commit type:REVERSE %% ECTTGS BPE>1

checkout "2|💾: basic pluggable EFCore" %% BPE
merge "3|💾: EFPropertyExpressionsCache" %% BPE
merge "2|💾: EFCore pessimistic locking" %% BPE
%% merge "2|💾: EFChangeTrackerTrackGraphStrategy" %% BPE
merge "2|💾: EFChangeTrackerTrackGraphStrategy" type:NORMAL tag:"2|💾|YourCompany.Configuration.EFCore|YourCompany.Configuration.EFCore.PostgreSQL|YourCompany.Configuration.EFCore.Sqlite" %% BPE
%% merge "2|💾: EFCore hosting migration run" type:NORMAL tag:"2|💾|YourCompany.Configuration.EFCore|YourCompany.Configuration.EFCore.PostgreSQL|YourCompany.Configuration.EFCore.Sqlite" %% BPE

%% checkout "3|🏠: OLTP RecordTypesCompositionMap" %% TCM
%% branch "2|🧱: TypesCompositionMap" %% TCM
%% commit type:REVERSE %% TCM>1
%% commit type:REVERSE %% TCM>2
%% commit type:REVERSE %% TCM>3
%% commit type:REVERSE %% TCM>4
%% commit type:REVERSE %% TCM>5
%% commit type:REVERSE %% TCM>6
%% commit type:REVERSE %% TCM>7
%% commit type:REVERSE %% TCM>8
%% commit type:NORMAL tag:"2|🧱|YourCompany.Reflection" %% TCM

checkout "3|🏠: OLTP UseCaseTypesMap" %% TAH
%% commit type:REVERSE %% TAH<1
%% commit type:REVERSE %% TAH<2
branch "2|🧱: TypeAbstractionsHelper" %% TAH
commit type:REVERSE %% TAH>1
commit type:REVERSE %% TAH>2
commit type:REVERSE %% TAH>3
commit type:REVERSE %% TAH>4
commit type:REVERSE %% TAH>5
commit type:NORMAL tag:"2|🧱|YourCompany.Reflection" %% TAH
%% merge "2|🧱: TypesCompositionMap" type:NORMAL tag:"2|🧱|YourCompany.Reflection" %% TAH

checkout "4|💾: EFCore SortingKey reading" %% VTH
%% commit type:REVERSE %% VTH<1
%% commit type:REVERSE %% VTH<2
%% commit type:REVERSE %% VTH<3
%% commit type:REVERSE %% VTH<4
branch "2|🧱: ValueTupleHelper" %% VTH
%% merge "4|💾: cache EFProperty from ValueTuple" %% VTH
commit type:REVERSE %% VTH>1
commit type:REVERSE %% VTH>2
commit type:REVERSE %% VTH>3
commit type:REVERSE %% VTH>4
commit type:REVERSE %% VTH>5
commit type:REVERSE %% VTH>6
commit type:REVERSE %% VTH>7
commit type:REVERSE %% VTH>8
commit type:REVERSE %% VTH>9
commit type:NORMAL tag:"2|🧱|YourCompany.Reflection" %% VTH

checkout "5|🧱: OLTP DI ScopedRecordsProvider" %% DCH
branch "2|🧱: DIConstructorHelper" %% DCH
commit type:REVERSE %% DCH>1
commit type:REVERSE %% DCH>2
commit type:REVERSE %% DCH>3
commit type:REVERSE %% DCH>4
commit type:REVERSE %% DCH>5
commit type:REVERSE %% DCH>6
commit type:REVERSE %% DCH>7
commit type:REVERSE %% DCH>8
commit type:REVERSE %% DCH>9
commit type:REVERSE %% DCH>10
commit type:REVERSE %% DCH>11
commit type:REVERSE %% DCH>12
commit type:REVERSE %% DCH>13
commit type:REVERSE %% DCH>14
commit type:REVERSE %% DCH>15
commit type:REVERSE %% DCH>16
commit type:REVERSE %% DCH>17
%% commit type:REVERSE %% DCH>18
%% commit type:REVERSE %% DCH>19
%% commit type:REVERSE %% DCH>20
%% commit type:REVERSE %% DCH>21
%% commit type:REVERSE %% DCH>22
commit type:NORMAL tag:"2|🧱|YourCompany.Reflection.DI" %% DCH

checkout "3|💾: EFPropertyExpressionsCache" %% Startup Iteration 2
commit type:REVERSE %% EPEC 2>1

%% checkout "2|🧱: TypeAbstractionsHelper" %% GMH
checkout "4|💾: EFEntityEntryPropertiesCache" %% GMH
branch "2|🧱: GetMemberHelper" %% GMH
merge "3|💾: EFPropertyExpressionsCache" %% GMH
%% commit type:NORMAL tag:"2|🧱|YourCompany.Reflection" %% GMH
merge "2|🧱: DIConstructorHelper" type:NORMAL tag:"2|🧱|YourCompany.Reflection" %% GMH

checkout "3|🏠: generic OLTP use cases" %% ATL
branch "2|🧱: AwaitTasksList" %% ATL
commit type:REVERSE %% ATL>1
commit type:REVERSE %% ATL>2
commit type:REVERSE %% ATL>3
commit type:REVERSE %% ATL>4
commit type:REVERSE %% ATL>5
commit type:NORMAL tag:"2|🧱|YourCompany.Threading" %% ATL

checkout "4|🧱: OLTP identities are unique keys" %% SH
branch "2|🧱: StructHelper" %% SH
commit type:REVERSE %% SH>1
commit type:REVERSE %% SH>2
commit type:REVERSE %% SH>3
commit type:REVERSE %% SH>4
commit type:REVERSE %% SH>5
commit type:REVERSE %% SH>6
commit type:REVERSE %% SH>7
commit type:REVERSE %% SH>8
commit type:REVERSE %% SH>9
commit type:REVERSE %% SH>10
commit type:REVERSE %% SH>11
commit type:REVERSE %% SH>12
commit type:REVERSE %% SH>13
commit type:REVERSE %% SH>14
%% commit type:REVERSE %% SH>15
%% commit type:REVERSE %% SH>16
%% commit type:REVERSE %% SH>17
%% commit type:REVERSE %% SH>18
commit type:NORMAL tag:"2|🧱|YourCompany.CompilerServices" %% SH

checkout "3|🧱: OLTP transaction structure" %% Startup Iteration 2
commit type:REVERSE %% OTS 2>1
commit type:REVERSE %% OTS 2>2
commit type:REVERSE %% OTS 2>3
commit type:REVERSE %% OTS 2>4
commit type:REVERSE %% OTS 2>5
commit type:REVERSE %% OTS 2>6
commit type:REVERSE %% OTS 2>7
commit type:REVERSE %% OTS 2>8
%% commit type:REVERSE %% OTS 2>9
%% commit type:REVERSE %% OTS 2>10
%% commit type:REVERSE %% OTS 2>11
%% commit type:REVERSE %% OTS 2>12

checkout "3|🏠: generic OLTP use cases" %% OTILS
%% checkout "3|🧱: OLTP transaction structure" %% OTILS
branch "2|🏠: OLTP transaction is limited size" %% OTILS
%% commit type:HIGHLIGHT tag:"2|🏠|oltp-ways-to-access-size-limited-record-batch|YourCompany.OLTP.RecordsManagement" %% OTILS
merge "3|🧱: OLTP transaction structure" type:HIGHLIGHT tag:"2|🏠|oltp-ways-to-access-size-limited-record-batch|YourCompany.OLTP.RecordsManagement" %% OTILS

%% Startup Iteration 1

%% checkout "MERGED" %% BCC
checkout "2|💾: basic pluggable EFCore" %% BCC
branch "1|🧱: basic configurations covered" %% BCC
commit type:REVERSE %% BCC>1
%% commit type:REVERSE %% BCC>2
%% commit type:REVERSE %% BCC>3
%% commit type:REVERSE %% BCC>4
commit type:HIGHLIGHT tag:"1|🧱|configuration-entry-assembly-rotating-secrets-scaling-plugins|YourCompany.Configuration" %% BCC

%% checkout "3|🏠: OLTP RecordTypesCompositionMap" %% OTC
%% commit type:REVERSE %% OTC<1
%% commit type:REVERSE %% OTC<2
%% commit type:REVERSE %% OTC<3
%% commit type:REVERSE %% OTC<4
%% commit type:REVERSE %% OTC<5
%% commit type:REVERSE %% OTC<6
%% commit type:REVERSE %% OTC<7
%% commit type:REVERSE %% OTC<8
%% commit type:REVERSE %% OTC<9
%% commit type:REVERSE %% OTC<10
%% commit type:REVERSE %% OTC<11
%% commit type:REVERSE %% OTC<12
%% commit type:REVERSE %% OTC<13
%% commit type:REVERSE %% OTC<14
%% commit type:REVERSE %% OTC<15
%% commit type:REVERSE %% OTC<16
%% commit type:REVERSE %% OTC<17
%% commit type:REVERSE %% OTC<18
%% commit type:REVERSE %% OTC<19
%% commit type:REVERSE %% OTC<20
%% branch "1|🏠: OLTP transactional composition" %% OTC
%% commit type:REVERSE %% OTC>1
%% commit type:REVERSE %% OTC>2
%% commit type:REVERSE %% OTC>3
%% commit type:REVERSE %% OTC>4
%% commit type:REVERSE %% OTC>5
%% commit type:REVERSE %% OTC>6
%% commit type:REVERSE %% OTC>7
%% commit type:REVERSE %% OTC>8
%% commit type:REVERSE %% OTC>9
%% commit type:REVERSE %% OTC>10
%% commit type:REVERSE %% OTC>11
%% commit type:REVERSE %% OTC>12
%% commit type:REVERSE %% OTC>13
%% commit type:REVERSE %% OTC>14
%% commit type:NORMAL tag:"1|🏠|YourCompany.OLTP.StateOwnership.TransactionalComposition" %% OTC

%% checkout "N|🧱: wip switch to chassis" %% OEP
%% commit type:REVERSE %% OEP<1
%% commit type:REVERSE %% OEP<2
%% commit type:REVERSE %% OEP<3
%% commit type:REVERSE %% OEP<4
%% commit type:REVERSE %% OEP<5
%% commit type:REVERSE %% OEP<6
%% commit type:REVERSE %% OEP<7
%% commit type:REVERSE %% OEP<8
%% commit type:REVERSE %% OEP<9
%% commit type:REVERSE %% OEP<10
%% commit type:REVERSE %% OEP<11
%% commit type:REVERSE %% OEP<12
%% commit type:REVERSE %% OEP<13
%% commit type:REVERSE %% OEP<14
%% commit type:REVERSE %% OEP<15
%% commit type:REVERSE %% OEP<16
%% commit type:REVERSE %% OEP<17
%% commit type:REVERSE %% OEP<18
%% commit type:REVERSE %% OEP<19
%% branch "1|🏠: OLTP events producing" %% OEP
%% commit type:REVERSE %% OEP>1
%% commit type:REVERSE %% OEP>2
%% commit type:REVERSE %% OEP>3
%% commit type:REVERSE %% OEP>4
%% commit type:REVERSE %% OEP>5
%% commit type:REVERSE %% OEP>6
%% commit type:REVERSE %% OEP>7
%% commit type:REVERSE %% OEP>8
%% commit type:REVERSE %% OEP>9
%% commit type:REVERSE %% OEP>10
%% commit type:REVERSE %% OEP>11
%% commit type:REVERSE %% OEP>12
%% commit type:REVERSE %% OEP>13
%% commit type:REVERSE %% OEP>14
%% commit type:REVERSE %% OEP>15
%% commit type:REVERSE %% OEP>16
%% commit type:REVERSE %% OEP>17
%% commit type:REVERSE %% OEP>18
%% commit type:REVERSE %% OEP>19
%% commit type:REVERSE %% OEP>20
%% commit type:REVERSE %% OEP>21
%% commit type:REVERSE %% OEP>22
%% commit type:REVERSE %% OEP>23
%% commit type:REVERSE %% OEP>24
%% commit type:REVERSE %% OEP>25
%% commit type:REVERSE %% OEP>26
%% commit type:REVERSE %% OEP>27
%% commit type:REVERSE %% OEP>28
%% commit type:REVERSE %% OEP>29
%% commit type:REVERSE %% OEP>30
%% commit type:REVERSE %% OEP>31
%% commit type:REVERSE %% OEP>32
%% commit type:REVERSE %% OEP>33
%% commit type:REVERSE %% OEP>34
%% commit type:REVERSE %% OEP>35
%% commit type:REVERSE %% OEP>36
%% commit type:NORMAL tag:"1|🏠|YourCompany.OLTP.StateOwnership.EventsProducing" %% OEP

checkout "2|🏠: OLTP transaction is limited size" %% Startup Iteration 1
commit type:REVERSE %% OTILS 1>1
%% commit type:REVERSE %% OTILS 1>2
%% commit type:REVERSE %% OTILS 1>3

%% checkout MERGED %% THOOIT
checkout "2|🏠: OLTP transaction is limited size" %% THOOIT
checkout "3|🏠: OLTP RecordTypesMap" %% THOOIT
branch "1|🏠: the heart of OLTP is transaction" %% THOOIT
%% merge "2|🏠: OLTP transaction is limited size" %% THOOIT
%% merge "1|🏠: OLTP events producing" %% THOOIT
%% merge "1|🏠: OLTP transactional composition" %% THOOIT
%% merge "N|🧱: wip switch to chassis" %% THOOIT
%% commit type:HIGHLIGHT tag:"1|🏠|oltp-basic-object-model-the-rest-is-up-to-you|YourCompany.OLTP.StateOwnership" %% THOOIT
merge "2|🏠: OLTP transaction is limited size" type:HIGHLIGHT tag:"1|🏠|oltp-basic-object-model-the-rest-is-up-to-you|YourCompany.OLTP.StateOwnership" %% THOOIT
Loading

Contact me

Still reading? 🥳 Hopefully the above and what expects you next
resonates to your professional experience 🤞

With any comments, thanks or other feedback please contact me at
gzorik.us@gmail.com.

Looking forward to a productive conversation with you 👀

Entry points

Now let's dive in into the entry point branches available. It's sorted
by complexity decrease top to bottom. The first block is the current
solution, the latest is a template.

Note when you checkout the particular entry point it won't contain
any fixups yet made after publishing. You'll only see the latter in a
next entry points graph version. See also the detailed notes in
Branching rules and Versioning.

Branch: oltp-di-efcore-handle-specifications-only-the-rest-is-covered

Files: 203 | Lines: 17310
Pros: out-of-the-box full-fledged persistence utilizing the linq record data builders
Cons: significant entry point complexity increase because of covering EFCore missing capabilities

Log Examples Modules
1|⁠🏠: the heart of OLTP is transaction  TODO  YourCompany.OLTP.StateOwnership (+371 lines)
1|⁠🧱: basic configurations covered  TODO  YourCompany.Configuration (+637 lines)
2|⁠🏠: OLTP transaction is limited size  TODO  YourCompany.OLTP.RecordsManagement (+1500 lines)
2|⁠🧱: StructHelper  TODO  YourCompany.CompilerServices (+18 lines)
2|⁠🧱: AwaitTasksList  TODO  YourCompany.Threading (+49 lines)
2|⁠🧱: GetMemberHelper  TODO  YourCompany.Reflection (+57 lines)
2|⁠🧱: DIConstructorHelper  TODO  YourCompany.Reflection.DI (+31 lines)
2|⁠🧱: ValueTupleHelper  TODO  YourCompany.Reflection (+143 lines)
2|⁠🧱: TypeAbstractionsHelper  TODO  YourCompany.Reflection (+26 lines)
2|⁠💾: basic pluggable EFCore  efcore‑migrations  YourCompany.Configuration.EFCore (+429 lines)
YourCompany.Configuration.EFCore.PostgreSQL (+181 lines)
YourCompany.Configuration.EFCore.Sqlite (+71 lines)
2|⁠💾: EFCore pessimistic locking  TODO  YourCompany.Configuration.EFCore (+222 lines)
YourCompany.Configuration.EFCore.PostgreSQL (+59 lines)
YourCompany.Configuration.EFCore.Sqlite (+47 lines)
2|⁠💾: EFChangeTrackerTrackGraphStrategy  TODO  YourCompany.Configuration.EFCore (+106 lines)
3|⁠🏠: OLTP RecordTypesMap  TODO  YourCompany.OLTP.StateOwnership.Reflection (+263 lines)
3|⁠🏠: generic OLTP use cases  TODO  YourCompany.OLTP.RecordsManagement.UseCases (+212 lines)
3|⁠🏠: OLTP UseCaseTypesMap  TODO  YourCompany.OLTP.RecordsManagement.UseCases.Reflection (+176 lines)
3|⁠🧱: OLTP transaction structure  TODO  YourCompany.OLTP.RecordsManagement (+1918 lines)
3|⁠💾: EFPropertyExpressionsCache  TODO  YourCompany.Configuration.EFCore (+87 lines)
3|⁠💾: EFCore first-class SortingKey  TODO  YourCompany.Configuration.EFCore (+2388 lines)
YourCompany.Configuration.EFCore.PostgreSQL (+37 lines)
YourCompany.Configuration.EFCore.Sqlite (+37 lines)
4|⁠🏠: enumerate OLTP use cases  TODO  YourCompany.OLTP.RecordsManagement.UseCases (+117 lines)
4|⁠🧱: OLTP identities are unique keys  TODO  YourCompany.OLTP.RecordsManagement.Persistence (+1642 lines)
4|⁠🧱: OLTP LINQ may be useful  TODO  YourCompany.OLTP.RecordsManagement.Persistence.Linq (+59 lines)
4|⁠💾: EFCore SortingKey reading  TODO  YourCompany.Configuration.EFCore (+789 lines)
4|⁠💾: EFEntityEntryPropertiesCache  TODO  YourCompany.Configuration.EFCore (+63 lines)
4|⁠💾: EFCore SortingKey ⇆ EntityEntry  TODO  YourCompany.Configuration.EFCore (+763 lines)
4|⁠💾: OLTP EFCore MetadataHelper  TODO  YourCompany.OLTP.StateOwnership.Reflection.EFCore (+45 lines)
4|⁠💾: OLTP EFCore ID generation  TODO  YourCompany.OLTP.StateOwnership.Reflection.EFCore (+111 lines)
4|⁠💾: EFEntityTypeSortingKeyTopology  TODO  YourCompany.Configuration.EFCore (+347 lines)
5|⁠🧱: OLTP switch to chassis  TODO  YourCompany.OLTP.RecordsManagement (+1 lines)
YourCompany.OLTP.RecordsManagement.DI (+122 lines)
YourCompany.OLTP.RecordsManagement.Persistence (-26 lines)
YourCompany.OLTP.RecordsManagement.UseCases (-117 lines)
YourCompany.OLTP.StateOwnership (+6 lines)
5|⁠🧱: OLTP DI ScopedRecordsProvider  TODO  YourCompany.OLTP.StateOwnership.Reflection.DI (+120 lines)
5|⁠🧱: OLTP DI ScopedUseCasesProvider  TODO  YourCompany.OLTP.RecordsManagement.UseCases.Reflection.DI (+69 lines)
5|⁠🧱: OLTP DI inheritance free repo  TODO  YourCompany.OLTP.RecordsManagement.DI (+444 lines)
5|⁠💾: OLTP EFCore readonly  TODO  YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore (+2256 lines)
6|⁠💾: OLTP EFCore finalized  TODO  YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore (+1108 lines)
7|⁠🧱: OLTP DI EFCore switch to chassis  TODO  YourCompany.Configuration.EFCore (+4 lines)
YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore (+1 lines)
7|⁠🧱: OLTP DI EFCore integrated  efcore‑migrations  YourCompany.Configuration.EFCore (0 lines)
YourCompany.OLTP.RecordsManagement.DI.EFCore (+319 lines)
YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore (+2 lines)

Branch: oltp-di-combine-use-case-and-persistence-interfaces-without-inheritance

Files: 82 | Lines: 7058
Pros: avoid inheriting per entity, contravariant use cases auto-injection, good starting point for adapters
Cons: still lacking major things like events producing, transactional composition (domain modularity), persistence

Log Examples Modules
1|⁠🏠: the heart of OLTP is transaction  TODO  YourCompany.OLTP.StateOwnership (+371 lines)
2|⁠🏠: OLTP transaction is limited size  TODO  YourCompany.OLTP.RecordsManagement (+1500 lines)
2|⁠🧱: StructHelper  TODO  YourCompany.CompilerServices (+18 lines)
2|⁠🧱: AwaitTasksList  TODO  YourCompany.Threading (+49 lines)
2|⁠🧱: GetMemberHelper  TODO  YourCompany.Reflection (+57 lines)
2|⁠🧱: DIConstructorHelper  TODO  YourCompany.Reflection.DI (+31 lines)
2|⁠🧱: TypeAbstractionsHelper  TODO  YourCompany.Reflection (+26 lines)
3|⁠🏠: OLTP RecordTypesMap  TODO  YourCompany.OLTP.StateOwnership.Reflection (+263 lines)
3|⁠🏠: generic OLTP use cases  TODO  YourCompany.OLTP.RecordsManagement.UseCases (+212 lines)
3|⁠🏠: OLTP UseCaseTypesMap  TODO  YourCompany.OLTP.RecordsManagement.UseCases.Reflection (+176 lines)
3|⁠🧱: OLTP transaction structure  TODO  YourCompany.OLTP.RecordsManagement (+1918 lines)
4|⁠🏠: enumerate OLTP use cases  TODO  YourCompany.OLTP.RecordsManagement.UseCases (+117 lines)
4|⁠🧱: OLTP identities are unique keys  TODO  YourCompany.OLTP.RecordsManagement.Persistence (+1642 lines)
4|⁠🧱: OLTP LINQ may be useful  TODO  YourCompany.OLTP.RecordsManagement.Persistence.Linq (+59 lines)
5|⁠🧱: OLTP switch to chassis  TODO  YourCompany.OLTP.RecordsManagement (+1 lines)
YourCompany.OLTP.RecordsManagement.DI (+122 lines)
YourCompany.OLTP.RecordsManagement.Persistence (-26 lines)
YourCompany.OLTP.RecordsManagement.UseCases (-117 lines)
YourCompany.OLTP.StateOwnership (+6 lines)
5|⁠🧱: OLTP DI ScopedRecordsProvider  TODO  YourCompany.OLTP.StateOwnership.Reflection.DI (+120 lines)
5|⁠🧱: OLTP DI ScopedUseCasesProvider  TODO  YourCompany.OLTP.RecordsManagement.UseCases.Reflection.DI (+69 lines)
5|⁠🧱: OLTP DI inheritance free repo  TODO  YourCompany.OLTP.RecordsManagement.DI (+444 lines)

Branch: oltp-di-combine-use-case-and-persistence-interfaces-switch-to-chassis

Files: 61 | Lines: 5872
Pros: reduced public surface and boilerplate, no all-in-one inheriting, jobs segregated, maintain your chassis separately
Cons: still requires repository per entity, lacks DI, events, transactional composition (domain modularity), persistence

Log Examples Modules
1|⁠🏠: the heart of OLTP is transaction  TODO  YourCompany.OLTP.StateOwnership (+371 lines)
2|⁠🏠: OLTP transaction is limited size  TODO  YourCompany.OLTP.RecordsManagement (+1500 lines)
2|⁠🧱: StructHelper  TODO  YourCompany.CompilerServices (+18 lines)
2|⁠🧱: AwaitTasksList  TODO  YourCompany.Threading (+49 lines)
3|⁠🏠: generic OLTP use cases  TODO  YourCompany.OLTP.RecordsManagement.UseCases (+212 lines)
3|⁠🧱: OLTP transaction structure  TODO  YourCompany.OLTP.RecordsManagement (+1918 lines)
4|⁠🏠: enumerate OLTP use cases  TODO  YourCompany.OLTP.RecordsManagement.UseCases (+117 lines)
4|⁠🧱: OLTP identities are unique keys  TODO  YourCompany.OLTP.RecordsManagement.Persistence (+1642 lines)
4|⁠🧱: OLTP LINQ may be useful  TODO  YourCompany.OLTP.RecordsManagement.Persistence.Linq (+59 lines)
5|⁠🧱: OLTP switch to chassis  TODO  YourCompany.OLTP.RecordsManagement (+1 lines)
YourCompany.OLTP.RecordsManagement.DI (+122 lines)
YourCompany.OLTP.RecordsManagement.Persistence (-26 lines)
YourCompany.OLTP.RecordsManagement.UseCases (-117 lines)
YourCompany.OLTP.StateOwnership (+6 lines)

Branch: oltp-segregate-persistence-repository-is-for-use-cases

Files: 56 | Lines: 5508
Pros: clarified identities and streamlined persistence implementation
Cons: may be not enough organized if you need to reuse some use cases

Log Examples Modules
1|⁠🏠: the heart of OLTP is transaction  TODO  YourCompany.OLTP.StateOwnership (+371 lines)
2|⁠🏠: OLTP transaction is limited size  TODO  YourCompany.OLTP.RecordsManagement (+1500 lines)
2|⁠🧱: StructHelper  TODO  YourCompany.CompilerServices (+18 lines)
3|⁠🧱: OLTP transaction structure  TODO  YourCompany.OLTP.RecordsManagement (+1918 lines)
4|⁠🧱: OLTP identities are unique keys  TODO  YourCompany.OLTP.RecordsManagement.Persistence (+1642 lines)
4|⁠🧱: OLTP LINQ may be useful  TODO  YourCompany.OLTP.RecordsManagement.Persistence.Linq (+59 lines)

Branch: oltp-segregate-use-cases-repository-is-for-persistence

Files: 47 | Lines: 4167
Pros: reuse authorizers, externalize records batch configuration, customize state access
Cons: still a lot of methods for persistence and complex records batch state

Log Examples Modules
1|⁠🏠: the heart of OLTP is transaction  TODO  YourCompany.OLTP.StateOwnership (+371 lines)
2|⁠🏠: OLTP transaction is limited size  TODO  YourCompany.OLTP.RecordsManagement (+1500 lines)
2|⁠🧱: AwaitTasksList  TODO  YourCompany.Threading (+49 lines)
3|⁠🏠: generic OLTP use cases  TODO  YourCompany.OLTP.RecordsManagement.UseCases (+212 lines)
3|⁠🧱: OLTP transaction structure  TODO  YourCompany.OLTP.RecordsManagement (+1918 lines)
4|⁠🏠: enumerate OLTP use cases  TODO  YourCompany.OLTP.RecordsManagement.UseCases (+117 lines)

Branch: oltp-typical-transaction-structure-repository-is-up-to-you

Files: 42 | Lines: 3789
Pros: have all control structured in one place
Cons: mixes use case related logic with persistence

Log Examples Modules
1|⁠🏠: the heart of OLTP is transaction  TODO  YourCompany.OLTP.StateOwnership (+371 lines)
2|⁠🏠: OLTP transaction is limited size  TODO  YourCompany.OLTP.RecordsManagement (+1500 lines)
3|⁠🧱: OLTP transaction structure  TODO  YourCompany.OLTP.RecordsManagement (+1918 lines)

Branch: oltp-ways-to-access-size-limited-record-batch

Files: 26 | Lines: 1871
Pros: enough to standardize use cases or "service 🙈" layer
Cons: repositories implemented per record type are too big

Log Examples Modules
1|⁠🏠: the heart of OLTP is transaction  TODO  YourCompany.OLTP.StateOwnership (+371 lines)
2|⁠🏠: OLTP transaction is limited size  TODO  YourCompany.OLTP.RecordsManagement (+1500 lines)

Branch: configuration-entry-assembly-rotating-secrets-scaling-plugins

Files: 10 | Lines: 637
Pros: not tied to typical component types, the most common
Cons: you may find another set of conventions more suitable

Log Examples Modules
1|⁠🧱: basic configurations covered  TODO  YourCompany.Configuration (+637 lines)

Branch: oltp-basic-object-model-the-rest-is-up-to-you

Files: 6 | Lines: 371
Pros: isolate your model from anything unrelated to your domain
Cons: too much boilerplate code being copied for each use case

Log Examples Modules
1|⁠🏠: the heart of OLTP is transaction  TODO  YourCompany.OLTP.StateOwnership (+371 lines)

Branch: template-entry-point-branch-name

Files: ### | Lines: #####
Pros: why do you choose this entry point
Cons: why it might be not enough for you

Log Examples Modules
replace_with_each_included_commit_subject  at‑least‑one‑example‑solution‑name 
 extra‑example‑solution‑name 
YourCompany.Framework.Assembly.Name1 (+diff lines)
YourCompany.Framework.Assembly.Name2 (-diff lines)
YourCompany.Framework.Assembly.NameN (0 lines)

The table above is written by the script while staying on a break during
the rebase: it reads the branch bound by the break from the rebase todo and
regenerates the whole block from the markers around it. Pros and cons are
left as authored, they are not derivable from the commits.

Both this and the commit logs script run from a single alias invoked before each break, in that order.

Commit logs

👇 Replaced or appended in ordered manner by the script.

Commit: 1|🏠: the heart of OLTP is transaction

6 files changed, 371 insertions(+)
src/YourCompany.OLTP.StateOwnership/
 +++++++ 177 |⁠ Identity.cs                                                                                                    
    ++++ 109 |⁠ IState.cs                                                                                                      
      ++ 54  |⁠ TransactionCallback.cs                                                                                         
       + 14  |⁠ IStateModifying.cs                                                                                             
       + 10  |⁠ IStateAccess.cs                                                                                                
       + 7   |⁠ ISpecification.cs                                                                                              

One atomic action. Heard about DDD "Aggregates"? These are classes
that represent one transaction per object lifecycle.

Typical transactions modeled consist of one or many modifying actions.
While overused in practice read-only actions is an edge case (giving
no relevancy guarantees of the data once accessed).

The mistake № 1 is to model aggregates over abilities and limitations
of an ORM framework directly. To solve this we're going to delegate
transactional guarantees from a model (or "services" 🙈) to a
dedicated state access interface. Respectively the model itself
abstracts its record data into a separate interface in a manner we're
going to support (that's why we say we own the framework).

The model accepts its record data's IStateAccess via constructor and
claims its IState either read-only or by providing a callback used
later to assert the modeled data transition from one state to another.
Once the access is complete the result is placed to DataAfterAccess.

In transition IStateModifying provides extra data representations:

  1. SettingDataProperties - represents properties to replace
    persisted values with new non-default-only values (and skip the rest).
    When the aggregate exposes a corresponding record data property
    defined with a setter, the latter sets the value precisely into the
    record data instance behind this state property.

    this simplest form of mapping replaces the need for numbers of
    DTOs-per-"command" as well as for redundant contracts with
    persistence layer for majority of cases but comes at a cost of the
    record data's interface to be mutable (defining property setters).
    With this approach you ensure encapsulation by either means of
    InternalsVisibleTo or by adding init only checks. Otherwise this
    state property can be omitted.

  2. LockedRecordData - provides access to the unchanged record data
    state with relevancy guarantee, i.e. remaining locked in the database.
    During the first callback it is always null letting to assert only
    replacing values and/or modifying specifications before accessing DB
    (💡 you can inherit specifications from EventArgs to decompose this
    assertion logic with extra callbacks). Once the data is locked against
    other transactions there will be one or more callbacks with
    LockedRecordData not null denoting the only legal and
    completely safe moment to assert existing data invariants 😲
    At last it becomes null again in the last and only callback,
    denoting the end of data access by this aggregate instance.

As mentioned above we expanded ISpecification purpose to become
an instruction for a persistence logic behind IStateModifying to
perform a transition from the current state to be matching the desired
(See ChangeDataToMatch vs MatchBeforeDataChanging methods).
You can learn more about the original read-only concept in any DDD book.

Finally, still keeping the DDD book open, what is an entity?
To be more precise how the aggregate of your model corresponds
to a modeled entity? It is the identity responsible for this.
What is it, really? No matter. Even if it does matter and you need
to perform some actions in your model with it - you should utilize
domain services concept delegating the details away to the factories.

Precisely, digging more into the concept of entity, how do you
"create" it? Factories? Not really. At least you don't need to build
the whole record state by the factory, but rather you'd demand
an identity for an "existed externally" entity to be registered 😲
Created or updated - it doesn't matter for the model. It will
handle not yet "created" entity's record data as simple as a regular
LockedRecordData instance with all properties unset 😲

Down the road we'll see how identities are represented and managed
within OLTP records management, as well opening a brand new way of
parameterizing your transactions. For now lets just keep in mind that
identity structure is not a domain concern.

Commit: 1|🧱: basic configurations covered

10 files changed, 637 insertions(+)
src/YourCompany.Configuration/
 +++++++ 182 |⁠ EnvironmentConventions.cs                                                                                      
   +++++ 122 |⁠ ConfigurationExtensions.cs                                                                                     
   +++++ 122 |⁠ YourCompanyPluginsLoader.cs                                                                                    
     +++ 75  |⁠ YourCompanyAssemblyLoadContext.cs                                                                              
      ++ 31  |⁠ AssemblyExtensions.cs                                                                                          
       + 29  |⁠ GitHelper.cs                                                                                                   
       + 11  |⁠ YourCompanyPluginsLoadingContext.cs                                                                            
       + 9   |⁠ YourCompanyPluginLoadingConfiguration.cs                                                                       
src/YourCompany.Configuration/KeyFileToBase64/
      ++ 41  |⁠ KeyFileToBase64ConfigurationProvider.cs                                                                        
       + 15  |⁠ KeyFileToBase64ConfigurationSource.cs                                                                          

⚠️ make sure your application entry project (one per solution) has
reference to Microsoft.Extensions.Configuration.UserSecrets package
and sets unique UserSecretsId beneath the TargetFramework ⚠️ that
serves as a marker to find an entry assembly which is a main convention.

EnvironmentConventions contains constants explaining the basics.
As of unusual features is GetYourCompanyInfraObjectNamesPrefix
IConfiguration extension method which allows to "switch environment
without switching infrastructure". This might be useful to either
utilize integrated infrastructure's own scaling abilities
(e.g. namespaces/topics) or simply organize zero-configuration start
for your developers letting to share the same testing environment that
comes out very handy sometimes.

As of plugins - one decision point is being "collectible". It's not
that relevant nowadays, since we work in containerized environments
and therefore we skip using it. For simplicity it's enough to use
single loading context per extension point, where it loads those of
assemblies required only which are not reachable down through the
entry assembly's dependencies.

Commit: 2|🏠: OLTP transaction is limited size

20 files changed, 1500 insertions(+)
src/YourCompany.OLTP.RecordsManagement/
 +++++++ 167 |⁠ IRecordsBatch.cs                                                                                               
   +++++ 135 |⁠ RecordsBatchTransactionCallback.cs                                                                             
   +++++ 125 |⁠ RecordsBatchTransactionConfiguration.ByIds.cs                                                                  
   +++++ 125 |⁠ RecordsBatchTransactionConfiguration.NoSorting.cs                                                              
    ++++ 98  |⁠ RecordsBatchTransactionConfiguration.NoSorting.Paginated.cs                                                    
    ++++ 90  |⁠ RecordsBatchTransactionExtensions.cs                                                                           
    ++++ 90  |⁠ RecordsBatchTransactionSpecification.ReadOnlyIncompatible.cs                                                   
    ++++ 85  |⁠ RecordsBatchTransactionConfiguration.ById.cs                                                                   
     +++ 83  |⁠ RecordsBatchTransactionSpecification.ReadOnlyIncompatible.SpecifiedRecord.cs                                   
     +++ 81  |⁠ RecordsBatchTransactionConfiguration.AfterId.cs                                                                
     +++ 76  |⁠ RecordsBatchTransactionConfiguration.cs                                                                        
     +++ 73  |⁠ RecordsBatchTransactionSpecification.cs                                                                        
     +++ 64  |⁠ RecordsBatchTransactionConfiguration.AfterId.Paginated.cs                                                      
      ++ 51  |⁠ RecordsBatchTransactionSpecification.SpecifiedRecordIncompatible.cs                                            
      ++ 50  |⁠ RecordsBatchTransactionSpecification.Sorting.cs                                                                
      ++ 47  |⁠ RecordsBatchTransactionSpecificationExtensions.cs                                                              
       + 23  |⁠ RecordsBatchTransaction.cs                                                                                     
       + 20  |⁠ RecordsBatchTransactionSpecification.ReadOnly.cs                                                               
       + 10  |⁠ IRepository.cs                                                                                                 
       + 7   |⁠ IRecordsBatchSizeLimit.cs                                                                                      

Long story short big transactions work bad. We need a way
to enforce the records number limit per any transaction run.

But the size is not the only "specification" to be considered.
In contrast to the DDD specifications these are the options
rather than the matching strategy. There are particular rules that
must be followed to be able to resolve a particular combination
of these options. Look for details in these rules implemented by
RecordsBatchTransactionSpecification semi-closed hierarchy.

Considering the compatibility rules and their nested long-name nature
its creation is streamlined into a fluent-API-like extensions set
in RecordsBatchTransactionExtensions.

Among all it's worth to highlight use cases and keyset pagination.

For use cases as we stated before it is typical to modify data.
That's why we design UseCaseParameters to switch any transaction
into modifying one. It is expected to declare a dedicated class per
use case
. It may contain some previously queried data or represent a
domain event. But there is also a special case allowing to turn any
transaction into modifying without any data being changed and later we
will see why. The special case is triggered by
TriggeredBeforeDataChanging event args, and there are also a few
ReadAndMatchExclusive extension methods.

For keyset pagination. It tightly relates to records ordering. To be
effective that ordering must be performed on the storage side.
That means we need to instruct the appropriate persistence adapter
to pick records after a particular unique key in a particular order.
And It turns out that previously introduced Identity is a perfect fit 🤔

The model asks a particular factory to provide a key Identity
for values it specifies while encapsulating a desired ordering by the
factory method name or extra parameters. This way all the persistence
concerns remain isolated from the model. If your use case or a
"service 🙈" logic requires to iterate more records through than the
records batch size limit allows then it may use the
TryGetLastPresentRecordIdentity extension to continue in another
transaction. Such key identity is sometimes called a "cursor".

Commit: 2|🧱: StructHelper

1 file changed, 18 insertions(+)
src/YourCompany.CompilerServices/
 +++++++ 18  |⁠ StructHelper.cs                                                                                                

Commit: 2|🧱: AwaitTasksList

1 file changed, 49 insertions(+)
src/YourCompany.Threading/
 +++++++ 49  |⁠ AwaitTasksList.cs                                                                                              

Commit: 2|🧱: GetMemberHelper

1 file changed, 57 insertions(+)
src/YourCompany.Reflection/
 +++++++ 57  |⁠ GetMemberHelper.cs                                                                                             

Commit: 2|🧱: DIConstructorHelper

1 file changed, 31 insertions(+)
src/YourCompany.Reflection.DI/
 +++++++ 31  |⁠ DIConstructorHelper.cs                                                                                         

Commit: 2|🧱: ValueTupleHelper

2 files changed, 143 insertions(+)
src/YourCompany.Reflection/
 +++++++ 113 |⁠ ValueTupleHelper.Expressions.cs                                                                                
      ++ 30  |⁠ ValueTupleHelper.cs                                                                                            

Commit: 2|🧱: TypeAbstractionsHelper

1 file changed, 26 insertions(+)
src/YourCompany.Reflection/
 +++++++ 26  |⁠ TypeAbstractionsHelper.cs                                                                                      

Commit: 2|💾: basic pluggable EFCore

11 files changed, 681 insertions(+)
src/YourCompany.Configuration.EFCore/
 +++++++ 145 |⁠ YourCompanyDbContext.DbObjectsPrefixing.cs                                                                     
   +++++ 117 |⁠ YourCompanyDbContext.cs                                                                                        
    ++++ 81  |⁠ YourCompanyDbContextFactory.cs                                                                                 
      ++ 34  |⁠ YourCompanyDbContextConfiguratorsLoadingContext.cs                                                             
       + 22  |⁠ YourCompanyDbContextConfigurator.cs                                                                            
       + 13  |⁠ ConfigurationExtensions.cs                                                                                     
       + 11  |⁠ YourCompanyDbContextConfiguration.cs                                                                           
       + 6   |⁠ YourCompanyDbContextFactoryLoadingConfiguration.cs                                                             
src/YourCompany.Configuration.EFCore.PostgreSQL/
   +++++ 115 |⁠ PrefixedObjectNamesShortener.cs                                                                                
     +++ 66  |⁠ NpgsqlDbContextConfigurator.cs                                                                                 
src/YourCompany.Configuration.EFCore.Sqlite/
     +++ 71  |⁠ SqliteDbContextConfigurator.cs                                                                                 

Firstly it supports both RunTime and DesignTime factories which
are usually troublesome to combine. See detailed in the example how to
make your migrations target this shared YourCompanyDbContext.

As of YourCompanyDbContext it accepts TConfiguration what allows
you to have multiple configurations and models respectively but
its both uncommon and not encouraged in practice (i.e. separate apps).

Also there is ConfiguredUtcNow which is useful in testing. And ofc
we support GetYourCompanyInfraObjectNamesPrefix by prefixing DB
objects within the connection string configured schema.

For demo provider plugins we've chosen PostgreSQL and Sqlite.

Commit: 2|💾: EFCore pessimistic locking

7 files changed, 328 insertions(+)
src/YourCompany.Configuration.EFCore/PessimisticLocking/
 +++++++ 117 |⁠ ProviderSpecificPessimisticLockingUpdateInterceptor.cs                                                         
    ++++ 60  |⁠ PessimisticLockingUpdateInterceptionContext.cs                                                                 
src/YourCompany.Configuration.EFCore.PostgreSQL/
     +++ 58  |⁠ NpgsqlPessimisticLockingUpdateInterceptor.cs                                                                   
       + 1   |⁠ NpgsqlDbContextConfigurator.cs                                                                                 
src/YourCompany.Configuration.EFCore.Sqlite/
     +++ 46  |⁠ SqlitePessimisticLockingUpdateInterceptor.cs                                                                   
       + 1   |⁠ SqliteDbContextConfigurator.cs                                                                                 
src/YourCompany.Configuration.EFCore/
     +++ 45  |⁠ YourCompanyDbContext.PessimisticLocking.cs                                                                     

Commit: 2|💾: EFChangeTrackerTrackGraphStrategy

1 file changed, 106 insertions(+)
src/YourCompany.Configuration.EFCore/ChangeTracking/
 +++++++ 106 |⁠ EFChangeTrackerTrackGraphStrategy.cs                                                                           

Commit: 3|🏠: OLTP RecordTypesMap

4 files changed, 263 insertions(+)
src/YourCompany.OLTP.StateOwnership.Reflection/
 +++++++ 97  |⁠ RecordConstructionHelper.cs                                                                                    
   +++++ 74  |⁠ RecordTypeInfo.cs                                                                                              
    ++++ 56  |⁠ RecordConstructionException.cs                                                                                 
     +++ 36  |⁠ RecordTypesMap.cs                                                                                              

Commit: 3|🏠: generic OLTP use cases

2 files changed, 212 insertions(+)
src/YourCompany.OLTP.RecordsManagement.UseCases/
 +++++++ 167 |⁠ RecordsBatchTransactionUseCases.RunningExtensions.cs                                                           
      ++ 45  |⁠ RecordsBatchTransactionUseCases.cs                                                                             

Easily reuse and combine authorizers, record property setters or
organize each transaction post processing and extend aggregate
boundaries (though still limited with module boundaries, we'll
improve this later).

Commit: 3|🏠: OLTP UseCaseTypesMap

2 files changed, 176 insertions(+)
src/YourCompany.OLTP.RecordsManagement.UseCases.Reflection/
 +++++++ 89  |⁠ UseCaseTypeInfo.cs                                                                                             
  ++++++ 87  |⁠ UseCaseTypesMap.cs                                                                                             

Commit: 3|🧱: OLTP transaction structure

17 files changed, 1919 insertions(+), 1 deletion(-)
src/YourCompany.OLTP.RecordsManagement/
 +++++++ 233 |⁠ RecordsBatchTransaction.WithRecords.WithRecordsData.cs                                                         
  ++++++ 212 |⁠ RecordsBatchTransaction.Specified.SetOnceProperties.cs                                                         
   +++++ 174 |⁠ RecordsBatchTransaction.WithIdentities.cs                                                                      
   +++++ 172 |⁠ RecordsBatchTransaction.Specified.Handling.cs                                                                  
   +++++ 160 |⁠ RecordsBatchTransaction.SpecifiedRun.cs                                                                        
    ++++ 154 |⁠ RecordsBatchTransaction.WithIdentities.ReplacingSpecified.cs                                                   
    ++++ 137 |⁠ RecordsBatchTransaction.WithRecords.WithRecordsData.RecordState.cs                                             
    ++++ 132 |⁠ RecordState.cs                                                                                                 
     +++ 107 |⁠ RecordsBatchTransaction.WithRecords.WithRecordsData.TriggerTransactionCallback.cs                              
     +++ 106 |⁠ RecordsBatchTransaction.SpecifiedRun.State.cs                                                                  
     +++ 95  |⁠ RecordsBatchTransaction.WithIdentities.Unique.cs                                                               
      ++ 59  |⁠ RecordsBatchTransaction.Specified.cs                                                                           
      ++ 58  |⁠ RecordsBatchTransaction.SpecifiedRun.TypicalSequence.cs                                                        
      ++ 58  |⁠ RecordsBatchTransaction.WithRecords.WithRecordsData.BuiltRecord.cs                                             
      ++ 39  |⁠ RecordsBatchTransaction.WithRecords.cs                                                                         
       + 22  |⁠ RecordsBatchTransaction.SpecifiedRun.RunOnceStep.cs                                                            
      +‑ 2   |⁠ RecordsBatchTransaction.cs                                                                                     

Be ready to implement following sequence per each record type:

  1. MaxRecordsInBatch
  2. HandleSpecifiedIdentities
  3. HandleSortingAfterId
  4. Authorize
  5. HandleReadOnlyRecordsIncludingBeforeRead
  6. HandleEachRecordSpecificationBeforeReadWithoutIds
  7. HandleEachRecordModifyingSpecificationBeforeRead
  8. ReadWithHandledEachRecordSpecifications
  9. GetReadRecordIdentity
  10. ValidateAssignedIdentityReplacementAfterReadByIds
  11. BuildRecord
  12. CreateRecordDataForSettingChangedProperties
  13. PrepareRecordsBatchChanges
  14. HandleSpecifiedRecordModifyingSpecificationBeforePersist
  15. PersistChanges
  16. GetLockedRecordDataWithoutChanges
  17. FinishRecordsDataAccess
  18. GetRecordDataAfterAccess
  19. HandleResultingRecordsBatch

Including a bunch of virtual methods, these give you ability to pause
between steps, make extra processing, etc. In short this entry point
is good for reimagining the framework in your own way. Later we're
going to significantly reduce the number of required methods to be
implemented by introducing DI and by segregating use case and
persistence related concerns.

Commit: 3|💾: EFPropertyExpressionsCache

2 files changed, 87 insertions(+)
src/YourCompany.Configuration.EFCore/ExpressionsCaching/
 +++++++ 50  |⁠ EFPropertyExpressionsCache.FromParameter.cs                                                                    
   +++++ 37  |⁠ EFPropertyExpressionsCache.cs                                                                                  

Commit: 3|💾: EFCore first-class SortingKey

36 files changed, 2464 insertions(+), 2 deletions(-)
src/YourCompany.Configuration.EFCore/CollationAwareSorting/
 +++++++ 177 |⁠ SortingKeyPropertiesVisitor.cs                                                                                 
  ++++++ 176 |⁠ SortingKeyPredicatesBuilder.cs                                                                                 
  ++++++ 160 |⁠ SortingKey.ValueHolding.ConvertedFrom.cs                                                                       
  ++++++ 155 |⁠ SortingKey.ValueHolding.cs                                                                                     
   +++++ 139 |⁠ ICollationAwareModelProvider.cs                                                                                
    ++++ 104 |⁠ SortingKeyBuilder.OrElse.ComparableValue.cs                                                                    
    ++++ 100 |⁠ SortingKey.Building.cs                                                                                         
    ++++ 95  |⁠ SortingKeyTopology.PropertiesOwner.cs                                                                          
    ++++ 94  |⁠ SortingKey.ToString.cs                                                                                         
    ++++ 94  |⁠ SortingKeyBuilder.ComparableValue.cs                                                                           
     +++ 86  |⁠ SortingKey.Querying.cs                                                                                         
     +++ 76  |⁠ OrElseEqualityKeys.Querying.cs                                                                                 
     +++ 73  |⁠ ICollationCompatibleComparersProvider.cs                                                                       
     +++ 69  |⁠ SortingKey.Comparison.cs                                                                                       
     +++ 69  |⁠ SortingKeyPredicatesBuilder.SingleEntityQuery.cs                                                               
     +++ 66  |⁠ SortingKey.cs                                                                                                  
     +++ 63  |⁠ OrElseEqualityKeys.cs                                                                                          
     +++ 60  |⁠ SortingKeyTopologyExtensions.cs                                                                                
      ++ 50  |⁠ SortingKeyQueries.cs                                                                                           
      ++ 47  |⁠ SortingKeyTopology.Comparer.cs                                                                                 
      ++ 44  |⁠ OrElseEqualityKeys.ToString.cs                                                                                 
      ++ 40  |⁠ SortingKeyTopology.cs                                                                                          
      ++ 35  |⁠ SortingKeyBuilder.cs                                                                                           
      ++ 32  |⁠ SortingKeyBuilder.OrElse.cs                                                                                    
       + 21  |⁠ SortingKey.Visiting.cs                                                                                         
src/YourCompany.Configuration.EFCore/CollationAwareSorting/Metadata/
   +++++ 123 |⁠ CollationAwareSortingSingleModelProvider.cs                                                                    
       + 18  |⁠ ModelExtensions.cs                                                                                             
src/YourCompany.Configuration.EFCore/
     +++ 80  |⁠ YourCompanyDbContext.SingleEntityQueries.cs                                                                    
       + 19  |⁠ YourCompanyDbContext.cs                                                                                        
       + 12  |⁠ YourCompanyDbContextConfiguratorsLoadingContext.cs                                                             
       + 9   |⁠ YourCompanyDbContextConfigurator.cs                                                                            
       + 2   |⁠ YourCompanyDbContextFactory.cs                                                                                 
src/YourCompany.Configuration.EFCore.PostgreSQL/
      ++ 33  |⁠ NpgsqlDbContextConfigurator.CollationAwareSorting.cs                                                           
      +‑ 6   |⁠ NpgsqlDbContextConfigurator.cs                                                                                 
src/YourCompany.Configuration.EFCore.Sqlite/
      ++ 33  |⁠ SqliteDbContextConfigurator.CollationAwareSorting.cs                                                           
      +‑ 6   |⁠ SqliteDbContextConfigurator.cs                                                                                 

This one is very missing out-of-the-box in EFCore. This replaces the
need to manually compose OrderBy ThenBy expressions allowing
comprehensive approaches to be implemented upon from query building
and keyset pagination to advanced change tracking operations.

Another covered problem is database strings collation having no analog
out-of-the-box neither in .NET nor in EFCore. In such circumstances
it is proper to delegate the missing logic to database provider
plugins which is exactly what was done.

Commit: 4|🏠: enumerate OLTP use cases

2 files changed, 117 insertions(+)
src/YourCompany.OLTP.RecordsManagement.UseCases/
 +++++++ 64  |⁠ RecordsBatchTransaction.UseCasesParallelRun.cs                                                                 
   +++++ 53  |⁠ RecordsBatchTransaction.cs                                                                                     

We've segregated use case related concerns into several interfaces.

The set of methods to override is slightly reduced for
RecordsBatchTransaction implementors:

  1. MaxRecordsInBatch
  2. HandleSpecifiedIdentities
  3. HandleSortingAfterId
  4. HandleReadOnlyRecordsIncludingBeforeRead
  5. HandleEachRecordSpecificationBeforeReadWithoutIds
  6. HandleEachRecordModifyingSpecificationBeforeRead
  7. ReadWithHandledEachRecordSpecifications
  8. GetReadRecordIdentity
  9. ValidateAssignedIdentityReplacementAfterReadByIds
  10. BuildRecord
  11. CreateRecordDataForSettingChangedProperties
  12. HandleSpecifiedRecordModifyingSpecificationBeforePersist
  13. PersistChanges
  14. GetLockedRecordDataWithoutChanges
  15. FinishRecordsDataAccess
  16. GetRecordDataAfterAccess

If your concern is reducing the number of methods and/or simplifying
persistence logic consider the alternative entry point branch
oltp-segregate-persistence-repository-is-for-use-cases where the
persistent logic is bridged into a state-sequenced set of
interfaces, making it much more streamlined to implement persistence.

To make both approaches work together we'll next introduce a DI
facade which will allow to avoid inheriting RecordsBatchTransaction
completely.

Commit: 4|🧱: OLTP identities are unique keys

11 files changed, 1642 insertions(+)
src/YourCompany.OLTP.RecordsManagement.Persistence/
 +++++++ 257 |⁠ RecordsDataAccess.Proxy.cs                                                                                     
  ++++++ 238 |⁠ RecordsDataAccess.State.FinishingChain.cs                                                                      
  ++++++ 227 |⁠ RecordsBatchTransactionDataBridge.cs                                                                           
  ++++++ 225 |⁠ RecordsDataAccess.State.cs                                                                                     
    ++++ 171 |⁠ UniqueKey.Primary.cs                                                                                           
    ++++ 157 |⁠ RecordsDataAccess.State.ReadIdentity.cs                                                                        
    ++++ 130 |⁠ RecordsDataAccess.cs                                                                                           
      ++ 85  |⁠ RecordsDataAccess.State.AfterSorting.cs                                                                        
      ++ 61  |⁠ UniqueKey.Alternate.cs                                                                                         
      ++ 50  |⁠ UniqueKey.cs                                                                                                   
       + 41  |⁠ UniqueKeyExtensions.cs                                                                                         

These might be primary, natural, or... prefixed. Remember afterId?
Keyset pagination? When we sort by some data columns we could
not necessarily use unique tuples, but rather just add a primary key
still allowing to utilize keyset pagination. Either way the query
result is expected to expose the so called "cursor" having the same
structure as the key parameter (afterId).

The mistake № 2 is to expose generated sequential ids, store,
serialize it outside the original storage, its btw a security concern 👻

The mistake № 3 is to violate aggregate boundaries by putting extra
foreign keys between aggregates still using those sequential ids.

UniqueKey semi-closed hierarchy takes into account all of these
and implements the major rules of identity lifecycle, such as
separating public and private PK as well as using predetermined
optimal PK types, i.e. long to be tolerant to continuous insertion
errors and Guid to be easily serializable and unified in a future
API surface.

As for RecordsBatchTransaction, or more precisely
RecordsBatchTransactionDataBridge bridging the state-sequenced set
of data access interfaces, you still need to implement several
required methods:

  1. Authorize
  2. BuildRecord
  3. PrepareRecordsBatchChanges
  4. HandleResultingRecordsBatch

These might be still not desired. You may find more useful the
oltp-segregate-use-cases-repository-is-for-persistence branch.

To make both approaches work together we'll next introduce a DI
facade which will allow to avoid inheriting RecordsBatchTransaction
completely.

Commit: 4|🧱: OLTP LINQ may be useful

2 files changed, 59 insertions(+)
src/YourCompany.OLTP.RecordsManagement.Persistence.Linq/
 +++++++ 49  |⁠ RecordDataQueryBuilder.cs                                                                                      
      ++ 10  |⁠ IRecordDataQueryBuildersProvider.cs                                                                            

It's a small extension to the streamlined data access interfaces
in the case one implements the transaction using linq-based
libs like EF or linq2db. So that the query building and modifying
specifications applying logic can be further decomposed.

Commit: 4|💾: EFCore SortingKey reading

10 files changed, 790 insertions(+), 1 deletion(-)
src/YourCompany.Configuration.EFCore/CollationAwareSorting/ExpressionsCaching/
 +++++++ 176 |⁠ SortingKeyQueriesCache.ForQuery.ForTopology.cs                                                                 
  ++++++ 171 |⁠ SortingKeyQueriesCache.ForQuery.ForTopology.TopologyVisitor.cs                                                 
     +++ 86  |⁠ SortingKeyQueriesCache.ForQuery.cs                                                                             
      ++ 58  |⁠ SortingKeyQueriesCache.ForQuery.ForTopology.TopologyVisitor.SingleEFProperty.cs                                
      ++ 56  |⁠ SortingKeyQueriesCache.ForQuery.ForTopology.TopologyVisitor.MultiEFProperties.ForEntity.cs                     
      ++ 51  |⁠ SortingKeyQueriesCache.ForQuery.ForTopology.TopologyVisitor.MultiEFProperties.cs                               
src/YourCompany.Configuration.EFCore/
   +++++ 132 |⁠ YourCompanyDbContext.SingleEntityQueries.cs                                                                    
src/YourCompany.Configuration.EFCore/CollationAwareSorting/
      ++ 51  |⁠ SortingKeyQueries.SingleEntityExtensions.cs                                                                    
       + 8   |⁠ SortingKeyExtraValuePair.cs                                                                                    
      +‑ 2   |⁠ SortingKeyQueries.cs                                                                                           

Commit: 4|💾: EFEntityEntryPropertiesCache

1 file changed, 63 insertions(+)
src/YourCompany.Configuration.EFCore/ChangeTracking/
 +++++++ 63  |⁠ EFEntityEntryPropertiesCache.cs                                                                                

Commit: 4|💾: EFCore SortingKey ⇆ EntityEntry

11 files changed, 764 insertions(+), 1 deletion(-)
src/YourCompany.Configuration.EFCore/CollationAwareSorting/ChangeTracking/
 +++++++ 127 |⁠ EntityEntryPropertiesCopyingVisitor.cs                                                                         
  ++++++ 116 |⁠ EntityEntryPropertiesCopyingVisitor.NonGeneric.cs                                                              
  ++++++ 110 |⁠ EntityEntrySortingKeyCreatingVisitor.cs                                                                        
   +++++ 105 |⁠ EntityEntryPropertiesCopyingVisitor.Generic.cs                                                                 
    ++++ 64  |⁠ EntityEntrySortingKeyCreatingVisitor.NonGeneric.cs                                                             
     +++ 61  |⁠ EntityEntryPropertiesSettingVisitor.NonGeneric.cs                                                              
     +++ 60  |⁠ EntityEntryPropertiesSettingVisitor.Generic.cs                                                                 
     +++ 55  |⁠ EntityEntrySortingKeyCreatingVisitor.Generic.cs                                                                
     +++ 48  |⁠ EntityEntryPropertiesSettingVisitor.cs                                                                         
src/YourCompany.Configuration.EFCore/CollationAwareSorting/
       + 17  |⁠ SortingKeyTopologyExtensions.ChangeTracking.cs                                                                 
      +‑ 2   |⁠ SortingKeyTopologyExtensions.cs                                                                                

Commit: 4|💾: OLTP EFCore MetadataHelper

1 file changed, 45 insertions(+)
src/YourCompany.OLTP.StateOwnership.Reflection.EFCore/
 +++++++ 45  |⁠ MetadataHelper.cs                                                                                              

Commit: 4|💾: OLTP EFCore ID generation

4 files changed, 112 insertions(+), 1 deletion(-)
src/YourCompany.OLTP.StateOwnership.Reflection.EFCore/
 +++++++ 79  |⁠ MetadataHelper.DelegatedPrimaryKeyGeneration.cs                                                                
      ++ 21  |⁠ RecordDataQueries.cs                                                                                           
       + 11  |⁠ DelegatedPrimaryKeyGenerationTable.cs                                                                          
      +‑ 2   |⁠ MetadataHelper.cs                                                                                              

Commit: 4|💾: EFEntityTypeSortingKeyTopology

7 files changed, 347 insertions(+)
src/YourCompany.Configuration.EFCore/CollationAwareSorting/Metadata/
 +++++++ 90  |⁠ EFEntityTypeSortingKeyTopology.Visiting.cs                                                                     
  ++++++ 83  |⁠ EFEntityTypeSortingKeyTopology.PrefixFirstPropertiesVisitCache.cs                                              
  ++++++ 77  |⁠ EFEntityTypeSortingKeyTopology.cs                                                                              
       + 11  |⁠ CollationAwareSortingSingleModelProvider.cs                                                                    
       + 4   |⁠ ModelExtensions.cs                                                                                             
src/YourCompany.Configuration.EFCore/CollationAwareSorting/
  ++++++ 80  |⁠ SortingKeyTopology.PrefixFirstPropertiesVisitCache.cs                                                          
       + 2   |⁠ ICollationAwareModelProvider.cs                                                                                

An edge case of the topology is all the entity properties.
Using previously introduced visiting abilities it will allow to
optimally implement some missing operations in EF.

Commit: 5|🧱: OLTP switch to chassis

35 files changed, 298 insertions(+), 312 deletions(-)
src/YourCompany.OLTP.RecordsManagement/
     +‑‑ 30  |⁠ RecordsBatchTransaction.SpecifiedRun.State.cs                                                                  
     +‑‑ 28  |⁠ RecordsBatchTransactionCallback.cs                                                                             
     +‑‑ 22  |⁠ RecordsBatchTransaction.WithRecords.WithRecordsData.BuiltRecord.cs                                             
      +‑ 16  |⁠ RecordState.cs                                                                                                 
      +‑ 14  |⁠ RecordsBatchTransactionSpecification.ReadOnlyIncompatible.cs                                                   
      +‑ 14  |⁠ RecordsBatchTransactionSpecification.SpecifiedRecordIncompatible.cs                                            
      +‑ 12  |⁠ RecordsBatchTransaction.Specified.cs                                                                           
      +‑ 10  |⁠ RecordsBatchTransaction.SpecifiedRun.RunOnceStep.cs                                                            
      +‑ 10  |⁠ RecordsBatchTransactionSpecification.ReadOnlyIncompatible.SpecifiedRecord.cs                                   
      +‑ 10  |⁠ RecordsBatchTransactionSpecification.Sorting.cs                                                                
      +‑ 8   |⁠ RecordsBatchTransactionSpecification.cs                                                                        
      +‑ 6   |⁠ RecordsBatchTransaction.WithRecords.WithRecordsData.cs                                                         
      +‑ 6   |⁠ RecordsBatchTransaction.WithRecords.WithRecordsData.TriggerTransactionCallback.cs                              
      +‑ 6   |⁠ RecordsBatchTransactionSpecification.ReadOnly.cs                                                               
      +‑ 5   |⁠ RecordsBatchTransaction.WithIdentities.cs                                                                      
      +‑ 4   |⁠ RecordsBatchTransaction.SpecifiedRun.cs                                                                        
      +‑ 4   |⁠ RecordsBatchTransaction.WithRecords.WithRecordsData.RecordState.cs                                             
      +‑ 4   |⁠ RecordsBatchTransactionSpecificationExtensions.cs                                                              
      +‑ 2   |⁠ RecordsBatchTransaction.Specified.Handling.cs                                                                  
      +‑ 2   |⁠ RecordsBatchTransaction.Specified.SetOnceProperties.cs                                                         
      +‑ 2   |⁠ RecordsBatchTransaction.SpecifiedRun.TypicalSequence.cs                                                        
      +‑ 2   |⁠ RecordsBatchTransaction.WithIdentities.ReplacingSpecified.cs                                                   
      +‑ 2   |⁠ RecordsBatchTransaction.WithIdentities.Unique.cs                                                               
      +‑ 2   |⁠ RecordsBatchTransaction.WithRecords.cs                                                                         
src/YourCompany.OLTP.RecordsManagement.Persistence/
   ++‑‑‑ 50  |⁠ RecordsDataAccess.Proxy.cs                                                                                     
     +‑‑ 32  |⁠ RecordsDataAccess.State.FinishingChain.cs                                                                      
      +‑ 20  |⁠ RecordsDataAccess.State.ReadIdentity.cs                                                                        
      +‑ 14  |⁠ RecordsDataAccess.State.AfterSorting.cs                                                                        
      +‑ 10  |⁠ RecordsDataAccess.State.cs                                                                                     
      +‑ 2   |⁠ RecordsBatchTransactionDataBridge.cs                                                                           
src/YourCompany.OLTP.RecordsManagement.DI/
 +++++++ 65  |⁠ RecordsBatchTransaction.UseCasesParallelRun.cs                                                                 
  ++++++ 57  |⁠ RecordsBatchTransaction.cs                                                                                     
src/YourCompany.OLTP.RecordsManagement.UseCases/
  ‑‑‑‑‑‑ 64  |⁠ RecordsBatchTransaction.UseCasesParallelRun.cs                                                                 
   ‑‑‑‑‑ 53  |⁠ RecordsBatchTransaction.cs                                                                                     
src/YourCompany.OLTP.StateOwnership/
     ++‑ 22  |⁠ TransactionCallback.cs                                                                                         

Here we internalize a lot of public classes and methods previously
open for overriding per project per entity. From now on we're
committed to classify any outstanding needs first while making
adjustments in your owned enterprise chassis repo (i.e. providing
nuget packages, ideally).

This allow us to switch into much more powerful approaches while
keeping the project's code as thin as possible, getting the most out
of the rule The less code doing the job, the more value it brings.
To put it simple, we want our projects to utilize the most out of
OOP/DDD with as less as possible infrastructure, glue, facading code.
In new features we're going to rely more on configurations for
potential overrides rather than implement them each time.

Note that RecordsBatchTransaction is made abstract temporarily. Next
we are going to introduce some reflection to completely avoid
its inheritance as well as utilize the semantics we already introduced
in the very beginning for our models accessing their state in a
delegated manner.

Commit: 5|🧱: OLTP DI ScopedRecordsProvider

4 files changed, 120 insertions(+)
src/YourCompany.OLTP.StateOwnership.Reflection.DI/
 +++++++ 80  |⁠ ScopedRecordsProvider.cs                                                                                       
      ++ 19  |⁠ SingletonStateAccess.cs                                                                                        
       + 12  |⁠ ICurrentStateAccess.cs                                                                                         
       + 9   |⁠ ICurrentStateAccessProvider.cs                                                                                 

Commit: 5|🧱: OLTP DI ScopedUseCasesProvider

1 file changed, 69 insertions(+)
src/YourCompany.OLTP.RecordsManagement.UseCases.Reflection.DI/
 +++++++ 69  |⁠ ScopedUseCasesProvider.cs                                                                                      

Commit: 5|🧱: OLTP DI inheritance free repo

8 files changed, 445 insertions(+), 1 deletion(-)
src/YourCompany.OLTP.RecordsManagement.DI/
 +++++++ 164 |⁠ RecordsBatchTransaction.CurrentStateAccess.cs                                                                  
     +++ 63  |⁠ YourCompanyServiceCollectionExtensions.cs                                                                      
     +++ 62  |⁠ ScopedRecordsBatchTransactionFactory.cs                                                                        
     +++ 56  |⁠ RecordsBatchTransaction.CurrentStateAccess.Provider.cs                                                         
      ++ 52  |⁠ RecordsBatchTransaction.CurrentlyResolvedRecord.cs                                                             
      ++ 35  |⁠ ScopedRepository.cs                                                                                            
       + 12  |⁠ IRecordsDataAccessProvider.cs                                                                                  
      +‑ 2   |⁠ RecordsBatchTransaction.cs                                                                                     

You never need to implement it anymore since your record's
(aggregate's) object creation is covered. The only required piece
IRecordsDataAccessProvider. This entry point is good for putting
first adapters in place as well as organizing full fledged ACID
transaction based model with optional use case types:

  • IAuthorizer
  • IChangesPreparer
  • IFinishingHandler
  • IExtraInterfacesProvider

Just call AddYourCompanyDomainTypes service collection extension and
you're all set. As of RecordTypesMap and UseCaseTypesMap you
simply put your types in your application startup. If the type accepts
IStateAccess or implements one of the supported use case types they
are registered as scoped and will be used by a resolved IRepository.

Commit: 5|💾: OLTP EFCore readonly

21 files changed, 2256 insertions(+)
src/YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore/
 +++++++ 219 |⁠ YourCompanyDbContextLockingRecordDataReader.cs                                                                 
  ++++++ 194 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.After.cs                              
  ++++++ 184 |⁠ YourCompanyDbContextLockingRecordDataReader.FactoriesCache.cs                                                  
   +++++ 182 |⁠ YourCompanyDbContextRecordsDataAccessAdapter.cs                                                                
   +++++ 177 |⁠ YourCompanyDbContextFactory.cs                                                                                 
   +++++ 166 |⁠ YourCompanyDbContext.DelegatedPrimaryKeys.cs                                                                   
   +++++ 164 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.cs                                         
    ++++ 143 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.cs                                    
    ++++ 128 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.FromNatural.cs          
    ++++ 116 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.FromPublic.cs           
    ++++ 116 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.cs                                               
     +++ 94  |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.cs                      
     +++ 92  |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.NoSorting.cs                          
      ++ 64  |⁠ YourCompanyDbContext.cs                                                                                        
      ++ 54  |⁠ YourCompanyDbContext.QueryableRecordDataTypes.cs                                                               
       + 31  |⁠ YourCompanyDbContext.DbContextLocking.cs                                                                       
       + 21  |⁠ YourCompanyDbContextConfiguratorsLoadingContext.cs                                                             
       + 16  |⁠ YourCompanyDbContextConfiguration.cs                                                                           
       + 14  |⁠ YourCompanyDbContextConfigurator.cs                                                                            
       + 7   |⁠ YourCompanyDbContextFactoryLoadingConfiguration.cs                                                             
src/YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore/CollationAwareSorting/
     +++ 74  |⁠ CollationAwareSortingUniqueKeyAdapter.cs                                                                       

This is a basic ReadOnly implementation of the records data access
adapter. At the core is a semi-closed hierarchy of
YourCompanyDbContextLockingRecordDataReader whose goal is to
encapsulate differences of accessing by ids and without. Later we'll
also introduce one for record data modifying part.

Here we also introduce DbContext locking contrary to the popular
belief that its strictly single threaded. It is actually, and
wherever you find logic decomposed, you'd see either
"scoped" DbContext injected at several "handlers" limiting parallel
processing completely or the "Unit-of-Work" limiting modularity
completely. But we'll do it different, allowing DbContext to be
locked by one of parallel conflicting tasks so that when it's one
turn it can benefit of the Local view (local caching of previously
accessed data, i.e. locked in the same transaction when previously
modified) and the same time not bounding processing to a single
DbContext instance as it's made when "scoped" (since we solely
use DbContext factories).

Commit: 6|💾: OLTP EFCore finalized

19 files changed, 1146 insertions(+), 38 deletions(-)
src/YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore/
  ++++++ 174 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ReadBeforeModifying.Finishing.cs                 
  ++++++ 162 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ReadBeforeModifying.cs                           
   +++++ 157 |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ReadBeforeModifying.Modifying.cs                 
   ++++‑ 135 |⁠ YourCompanyDbContextRecordsDataAccessAdapter.cs                                                                
    ++++ 104 |⁠ YourCompanyDbContext.PessimisticLocking.cs                                                                     
    +++‑ 97  |⁠ YourCompanyDbContext.DelegatedPrimaryKeys.cs                                                                   
     ++‑ 77  |⁠ YourCompanyDbContextLockingRecordDataReader.cs                                                                 
      +‑ 18  |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.cs                                               
       + 10  |⁠ YourCompanyDbContextConfiguration.cs                                                                           
       + 6   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.cs                                         
      +‑ 4   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.cs                      
      +‑ 4   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.FromNatural.cs          
      +‑ 4   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.FromPublic.cs           
      +‑ 2   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.After.cs                              
      +‑ 2   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.cs                                    
      +‑ 2   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.NoSorting.cs                          
       + 1   |⁠ YourCompanyDbContext.QueryableRecordDataTypes.cs                                                               
src/YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore/CollationAwareSorting/
 +++++++ 191 |⁠ EntityEntryModifyingVisitor.cs                                                                                 
      ++ 34  |⁠ EntityEntryModifyingPair.cs                                                                                    

This is a modifying finalization for the previously implemented record
data access adapter, adding an extra inheritor to the semi-closed
hierarchy of YourCompanyDbContextLockingRecordDataReader whose
responsibility is now slightly extended.

Worth mentioning that by extending its responsibility we're actually
following the EFCore's ideology of two DbCommand per one logical
transaction
(i.e. read first and then change and save). This
ideology may cost a lot in practice of high loaded apps and EFCore is
often avoided for that reason. Why we still implement this? 🤔 To show
what layer it really belongs to in the system as well as still
benefit the change tracking capabilities to utilize it as smart ORM
rather map changes to the "UPDATE setters" ourselves (btw, linq2db
is a perfect candidate for the latter).

Furthermore, to not keep silent about this, we're switching to the
pessimistic locking right away 😲 and by doing this we denote one of
the major OLTP rules - the transaction have to be fast and small.
We're not going to allow "to read first and then make sizeless logic"
on the contrary we're deliberately bound by a single repository call
UoW
that at its worst allows to put extra transactional logic
within its use case strategies, i.e. time and context bound.

Commit: 7|🧱: OLTP DI EFCore switch to chassis

51 files changed, 226 insertions(+), 221 deletions(-)
src/YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore/
 +++‑‑‑‑ 42  |⁠ YourCompanyDbContextLockingRecordDataReader.cs                                                                 
   ++‑‑‑ 30  |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.cs                                         
     +‑‑ 16  |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.After.cs                              
     +‑‑ 16  |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.NoSorting.cs                          
      +‑ 12  |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ReadBeforeModifying.cs                           
      +‑ 10  |⁠ YourCompanyDbContext.PessimisticLocking.cs                                                                     
      +‑ 10  |⁠ YourCompanyDbContextFactory.cs                                                                                 
      +‑ 8   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ReadBeforeModifying.Modifying.cs                 
      +‑ 6   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.cs                      
      +‑ 6   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.FromNatural.cs          
      +‑ 6   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ByIds.ResolvePrivateKeys.FromPublic.cs           
      +‑ 6   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.cs                                               
      +‑ 6   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.ReadBeforeModifying.Finishing.cs                 
      +‑ 6   |⁠ YourCompanyDbContextLockingRecordDataReader.SingleEntityQuery.WithoutIds.cs                                    
      +‑ 4   |⁠ YourCompanyDbContext.DelegatedPrimaryKeys.cs                                                                   
      +‑ 4   |⁠ YourCompanyDbContextRecordsDataAccessAdapter.cs                                                                
      +‑ 3   |⁠ YourCompanyDbContext.cs                                                                                        
      +‑ 2   |⁠ YourCompanyDbContext.DbContextLocking.cs                                                                       
      +‑ 2   |⁠ YourCompanyDbContext.QueryableRecordDataTypes.cs                                                               
      +‑ 2   |⁠ YourCompanyDbContextLockingRecordDataReader.FactoriesCache.cs                                                  
src/YourCompany.Configuration.EFCore/CollationAwareSorting/ChangeTracking/
     +‑‑ 16  |⁠ EntityEntryPropertiesCopyingVisitor.Generic.cs                                                                 
     +‑‑ 16  |⁠ EntityEntryPropertiesCopyingVisitor.NonGeneric.cs                                                              
      +‑ 10  |⁠ EntityEntryPropertiesSettingVisitor.Generic.cs                                                                 
      +‑ 10  |⁠ EntityEntryPropertiesSettingVisitor.NonGeneric.cs                                                              
      +‑ 10  |⁠ EntityEntrySortingKeyCreatingVisitor.Generic.cs                                                                
      +‑ 8   |⁠ EntityEntrySortingKeyCreatingVisitor.NonGeneric.cs                                                             
      +‑ 6   |⁠ EntityEntrySortingKeyCreatingVisitor.cs                                                                        
      +‑ 2   |⁠ EntityEntryPropertiesCopyingVisitor.cs                                                                         
      +‑ 2   |⁠ EntityEntryPropertiesSettingVisitor.cs                                                                         
src/YourCompany.Configuration.EFCore/CollationAwareSorting/
    ++‑‑ 24  |⁠ SortingKeyPropertiesVisitor.cs                                                                                 
     +‑‑ 14  |⁠ OrElseEqualityKeys.cs                                                                                          
      +‑ 10  |⁠ OrElseEqualityKeys.Querying.cs                                                                                 
      +‑ 10  |⁠ SortingKey.Querying.cs                                                                                         
      +‑ 4   |⁠ ICollationCompatibleComparersProvider.cs                                                                       
      +‑ 4   |⁠ SortingKey.Visiting.cs                                                                                         
      +‑ 2   |⁠ OrElseEqualityKeys.ToString.cs                                                                                 
      +‑ 2   |⁠ SortingKey.Building.cs                                                                                         
      +‑ 2   |⁠ SortingKey.ValueHolding.ConvertedFrom.cs                                                                       
      +‑ 2   |⁠ SortingKey.ValueHolding.cs                                                                                     
      +‑ 2   |⁠ SortingKeyPredicatesBuilder.cs                                                                                 
src/YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore/CollationAwareSorting/
     +‑‑ 16  |⁠ EntityEntryModifyingVisitor.cs                                                                                 
      +‑ 10  |⁠ EntityEntryModifyingPair.cs                                                                                    
src/YourCompany.Configuration.EFCore/ChangeTracking/
     +‑‑ 14  |⁠ EFChangeTrackerTrackGraphStrategy.cs                                                                           
      +‑ 6   |⁠ EFEntityEntryPropertiesCache.cs                                                                                
src/YourCompany.Configuration.EFCore/ExpressionsCaching/
      +‑ 12  |⁠ EFPropertyExpressionsCache.FromParameter.cs                                                                    
      +‑ 8   |⁠ EFPropertyExpressionsCache.cs                                                                                  
src/YourCompany.Configuration.EFCore/
      +‑ 8   |⁠ YourCompanyDbContextFactory.cs                                                                                 
      +‑ 6   |⁠ YourCompanyDbContext.cs                                                                                        
      +‑ 4   |⁠ YourCompanyDbContext.PessimisticLocking.cs                                                                     
src/YourCompany.Configuration.EFCore/PessimisticLocking/
      +‑ 8   |⁠ PessimisticLockingUpdateInterceptionContext.cs                                                                 
src/YourCompany.Configuration.EFCore/CollationAwareSorting/Metadata/
      +‑ 2   |⁠ EFEntityTypeSortingKeyTopology.cs                                                                              

We continue to internalize public classes and methods previously
open for overriding per project and for using extra capabilites
implemented over original EFCore. The rationale may be found in the
earlier commit.

Commit: 7|🧱: OLTP DI EFCore integrated

9 files changed, 323 insertions(+), 2 deletions(-)
src/YourCompany.OLTP.RecordsManagement.DI.EFCore/
 +++++++ 136 |⁠ YourCompanyServiceCollectionExtensions.cs                                                                      
   +++++ 102 |⁠ ScopedYourCompanyDbContextFactory.cs                                                                           
     +++ 54  |⁠ ScopedRecordsDataAccessProvider.cs                                                                             
       + 14  |⁠ YourCompanyDbContextConfigurator.cs                                                                            
       + 7   |⁠ YourCompanyDbContextFactoryLoadingConfiguration.cs                                                             
       + 6   |⁠ YourCompanyDbContextConfiguration.cs                                                                           
src/YourCompany.OLTP.RecordsManagement.Persistence.Linq.EFCore/
       + 2   |⁠ YourCompanyDbContextRecordsDataAccessAdapter.cs                                                                
      +‑ 2   |⁠ YourCompanyDbContextFactory.cs                                                                                 
src/YourCompany.Configuration.EFCore/
      +‑ 2   |⁠ YourCompanyDbContextFactory.cs                                                                                 

This entry point benefits from the plugin based EFCore initialization
letting to avoid manually building the record types map when you
configure your domain types and repositories as well as allowing
plugins to inject extra record data query builders both scoped and
singleton.

As of the huge gap in lines with the previous entry point we can
justify it by missing EFCore capabilities that were implemented in the
merged libraries:

  • properly unified RunTime and DesignTime DbContext factories
    working the same way both for queries and migrations for multiple
    switchable database providers;
  • collation aware SortingKey unifying modeled columns DB side
    comparison with .NET in-memory comparers and providing advanced
    querying abilities;
  • advanced abilities around SortingKey topology, allowing required
    change tracking strategies such as partial modifying by non-default
    properties set in the state and exposing public cursors while
    utilizing keyset pagination capabilities (AfterId);
  • ofc imlementing the segregated record data access interfaces which
    are described in more details in the corresponding commits.

About

Transaction-centric OLTP reference architecture for .NET. Eliminates read-before-write races, isolates domain from infrastructure limitations, and models aggregates as pure closures over state.

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Contributors

Languages