Skip to content

Support custom aggregator#331

Merged
jeremydmiller merged 2 commits into
JasperFx:masterfrom
tim-cools:support-custom-aggregator
Jun 14, 2016
Merged

Support custom aggregator#331
jeremydmiller merged 2 commits into
JasperFx:masterfrom
tim-cools:support-custom-aggregator

Conversation

@tim-cools
Copy link
Copy Markdown
Contributor

This update makes the event Aggregator fully pluggable. This was not possible because the implementation was used instead of the interface in many places.

I use it to support a different way to build aggregates. (See https://gist.github.com/tim-cools/694ba34dbb93b7a0cdf710886aa29275)

@jeremydmiller
Copy link
Copy Markdown
Member

A couple thoughts:

  • Why are you taking in an IDocumentSession? We aren't setting ourselves up for some N+ problems are we?
  • One of the reasons I was staying with that concrete Aggregator<T> was so that we could return it in the StoreOptions methods that add the aggregator, then chain a fluent interface behind that so you could add customizations or other steps to the aggregator. If we go for IAggregator<T>, then I think we need a new overload that allows you to customize the basic, concrete Aggregator<T>

@tim-cools
Copy link
Copy Markdown
Contributor Author

Good feedback...

  • My aggregate contains properties that represent AggregateEvents. So the publish and handling of the events done from inside the aggregate. New events are published to the stream when they are published from the aggregate. I don't want to make DB calls in the build method, but I need the session to add those events to the session.
    Other solutions would be:
  • Ah I see. I didn't see any other public members that were used. What other config are you thinking about?

@tim-cools
Copy link
Copy Markdown
Contributor Author

Second thought after reading your remark on the chat: it might be enough for me to pass in an interface that allows appending events.

@tim-cools
Copy link
Copy Markdown
Contributor Author

@jeremydmiller I believe you not convinced about taking this in. What is holding you back? Is it the passing of the session?

What if I only do the first modification with the IAggregate? This way a consumer can choose how the aggregate is rebuild. I can also look into returning the Aggregator for configuration.

@jeremydmiller
Copy link
Copy Markdown
Member

Sorry @tim-cools, this has been more of a get-around to it kind of thing. I'll have this in soon.

@tim-cools
Copy link
Copy Markdown
Contributor Author

@jeremydmiller no worries. let me know if have have concerns...

@tim-cools
Copy link
Copy Markdown
Contributor Author

Actually I believe we also have to support the 'events in a base class' scenario as it is often used in examples and DDD frameworks...

I created a separated issue for that: #369

@jvdvleuten
Copy link
Copy Markdown
Contributor

I don't think storing or publishing events is the responsibility of the aggregate. Complicates a lot of thinks.

@tim-cools
Copy link
Copy Markdown
Contributor Author

@jvdvleuten by storing above I didn't meant in database but in-memory in a list, like you did in the example you posted in the other issue by using the AgregateRoot base class. But having to use that base class is something that bothers me for a long time. And about the publish from an aggregate I mostly agree that it increases complexity.

I always look for ways to make my model more pure. So I'm exploring other ways to handle domain events by modelling the events explicitly and let the infrastructure take care of capturing the events and publishing it to to event store...

@jvdvleuten
Copy link
Copy Markdown
Contributor

@tim-cools that's true. Where in your example are you using the ISession in your Aggregator? Couldn't find it anymore.

@jeremydmiller jeremydmiller merged commit 1bb667d into JasperFx:master Jun 14, 2016
jeremydmiller added a commit that referenced this pull request May 20, 2026
…, partial #4527) (#4529)

* Bump JasperFx 2.0.0-rc.2 + Weasel 9.0.0-alpha.8 for the dedupe consume cycle

Foundation bump for the Critter Stack 2026 dedupe pillar (#214)
consume work. JasperFx / JasperFx.Events / SourceGenerator / SourceGeneration
-> 2.0.0-rc.2; Weasel.Postgresql / Weasel.Core / Weasel.EntityFrameworkCore
-> 9.0.0-alpha.8.

These rc/alpha packages lift a batch of types Marten previously forked
(TenancyStyle, DeleteStyle, metadata markers, IPatchExpression, the
projection-coordinator base, Hi-Lo sequence base, exceptions, etc). This
commit only moves the pins; the following commits consume each lifted type
and type-forward the old Marten names. The tree does not build green until
that consume sequence lands (the lifted types collide with Marten's copies
until each is resolved).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4517: consume lifted JasperFx TenancyStyle + DeleteStyle

#327 lifted TenancyStyle -> JasperFx.MultiTenancy.TenancyStyle and
DeleteStyle -> JasperFx.DeleteStyle (ordinals unchanged: Single=0/Conjoined=1,
Remove=0/SoftDelete=1). Delete Marten's byte-identical copies and alias the old
names via global using, matching the OperationRole / SnapshotLifecycle pattern
already in GlobalUsings.cs. Fixed the two fully-qualified Storage.TenancyStyle
references + one xmldoc cref.

Closes #4517.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4519: consume lifted SkippedEventsCountObserver + ResilientEventLoader

#329 lifted SkippedEventsCountObserver, ResilientEventLoader, and
EventLoaderException into JasperFx.Events.Daemon. Delete Marten's copies and
consume the lifted versions:

- MartenDatabase subscribes the lifted SkippedEventsCountObserver (Marten's HWM
  guard was the one kept upstream — behavior identical).
- BuildEventLoader constructs the lifted ResilientEventLoader with its new
  3-arg ctor (ResiliencePipeline, IEventLoader inner, IEventDatabase database),
  passing the shard database.
- EventLoaderException now resolves to the lifted type (base changed
  MartenException -> Exception); the only catch site is the Polly
  .Handle<EventLoaderException>() in ResilientPipelineBuilderExtensions, which
  now imports JasperFx.Events.Daemon. Nothing catches it as MartenException.

Marten core builds clean. Closes #4519.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4520: consume lifted JasperFx.Metadata markers (ISoftDeleted/IVersioned/ITracked)

#330 lifted ISoftDeleted, IVersioned, ITracked into JasperFx.Metadata
(ITracked carries Marten's exact non-nullable string shape). Delete Marten's
byte-identical copies and alias the old names via global using, matching the
TenancyStyle / DeleteStyle pattern. Marten's marker detection
(CanBeCastTo<ISoftDeleted>, `entity is ISoftDeleted`, the metadata policies)
now binds the single canonical JasperFx type, so document detection is
unchanged. IRevisioned is intentionally excluded (int-vs-long, handled under
#4526/#4528).

Closes #4520.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4521: consume lifted JasperFx.Events IPatchExpression<T> + RemoveAction

#331 lifted IPatchExpression<T> (Marten's canonical superset, verbatim)
and RemoveAction into JasperFx.Events. Delete Marten's copies; PatchExpression<T>
and the Patch<T>() extensions now implement/return the lifted interface
(import JasperFx.Events). RemoveAction (enum) is aliased via global using; the
open-generic IPatchExpression<T> cannot be aliased so the two consumer files
import the namespace directly. The self-returning fluent shape rules out a thin
derived-interface shim (implicit interface impl needs exact return-type match),
so this is a true consume.

Closes #4521.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4523: consume lifted JasperFx.Events.IDocumentSchemaResolver

#333 lifted IDocumentSchemaResolver to JasperFx.Events (contract
verbatim from Marten's — all 7 members identical). Delete Marten's copy and
alias the old name via global using; StoreOptions still implements it (its
explicit interface implementations bind through the alias), and
IReadOnlyStoreOptions.Schema keeps its return type.

Closes #4523.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4522: derive OpenTelemetryOptions from JasperFx base + alias TrackLevel

#332 lifted the common OpenTelemetryOptions base + TrackLevel to
JasperFx.OpenTelemetry. Marten.Services.OpenTelemetryOptions now derives from
JasperFx.OpenTelemetry.OpenTelemetryOptions via : base("Marten"), dropping the
duplicated TrackConnections + Meter (now inherited) while keeping the
changeset-metrics members (Applications, ExportCounterOnChangeSets,
TrackEventCounters, MartenCommitMetrics) which use the inherited Meter. The
name is unchanged so no type-forward is needed. TrackLevel is aliased via
global using.

Closes #4522.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4518: consume lifted DcbConcurrencyException + centralize dedupe aliases

#328 lifted DcbConcurrencyException (now : JasperFx.ConcurrencyException)
into JasperFx.Events. Delete Marten's copy; AssertDcbConsistency binds the
lifted type (it already imports JasperFx.Events). ProgressionProgressOutOfOrder
Exception was already the shared type (Marten used the ShardName ctor) — no
change.

Because propagating all the dedupe type-forwards (TenancyStyle, DeleteStyle,
metadata markers, RemoveAction, IDocumentSchemaResolver, TrackLevel,
DcbConcurrencyException, IStorageOperation, OperationRole, SnapshotLifecycle)
across Marten's test/extension projects would otherwise duplicate alias blocks
in many GlobalUsings.cs files, centralize them in src/Shared/DedupeAliases.cs,
linked into every JasperFx-referencing .csproj via Directory.Build.props
(ImplicitUsings is off repo-wide, so MSBuild <Using> items don't apply — a
linked C# global-using file is used). Removed the now-redundant per-project
GlobalUsings alias blocks; F#/netstandard2.0/LinqTestsTypes (base-JasperFx only)
are excluded.

Closes #4518.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4525: route ID resolution through JasperFx.DocumentIdentity + forward IdentityAttribute

#335 lifted JasperFx.IdentityAttribute + a side-effect-free
JasperFx.DocumentIdentity.FindIdMember(Type, Func<Type,bool>). Delete Marten's
empty MartenAttribute marker IdentityAttribute and alias the old name; it was
only consumed via HasAttribute<IdentityAttribute>() (the MartenAttribute
pipeline called a no-op Modify on it, so dropping the base changes nothing).

DocumentMapping.FindIdMember now delegates to the lifted helper, passing
Marten's own IsValidIdentityType predicate (which also recognizes strong-typed
ids + F# DUs). The lifted traversal + GetProperties are byte-identical to
Marten's prior implementation, so ID resolution behavior is unchanged; the now-
dead private GetProperties helper is removed. Fixed three tests that used the
fully-qualified Marten.Storage.TenancyStyle (now aliased).

Closes #4525.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4527 (partial): consume lifted Weasel.Core serialization enums (Casing, NonPublicMembersStorage)

Weasel 9.0.0-alpha.8 lifted the Casing and NonPublicMembersStorage enums into
Weasel.Core (weasel#287). Delete Marten's byte-identical copies from
ISerializer.cs and alias the old names in the shared dedupe file. Members are
unchanged (Default/CamelCase/SnakeCase; the [Flags] non-public storage set).

This is the forced serialization-enum subset of #4527 (the bump won't compile
without it). The Hi-Lo sequence base refactor (HiloSequenceBase) — the
behavior-sensitive remainder of #4527 — is deferred to the follow-up PR with
#4516 and #4526/#4528.

Refs #4527.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* #4524: re-base IInitialData + IConfigureMarten(+async) on lifted JasperFx generics

#334 lifted IInitialData<TStore>, IConfigureStore<TOptions>, and
IAsyncConfigureStore<TOptions> to JasperFx core. Re-base Marten's interfaces on
the generic contracts by inheritance (preserving the names and every existing
implementer, rather than deleting):

- Marten.Schema.IInitialData : JasperFx.IInitialData<IDocumentStore>
  (Populate(IDocumentStore, CancellationToken) is the closed generic member;
  MartenActivator + existing seed-data impls unchanged).
- IConfigureMarten : JasperFx.IConfigureStore<StoreOptions>
  (Configure(IServiceProvider, StoreOptions) inherited).
- IAsyncConfigureMarten : JasperFx.IAsyncConfigureStore<StoreOptions>
  (Configure(StoreOptions, CancellationToken) inherited).
- IConfigureMarten<T> keeps its marker role (transitively : IConfigureStore<StoreOptions>).

The AddMarten() factory's IConfigureMarten discovery binds the inherited
Configure unchanged; full solution builds clean across net9/net10.

Closes #4524.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants