Make missing Chronicle setup obvious: analyzer, runtime error, and docs#2322
Merged
Conversation
When AddCratisArc is used without WithChronicle, a command, provide method,
validator, or query that depends on a Chronicle service such as IEventLog
failed with a raw container exception ("Unable to resolve service for type
EventStoreName...") that read like a framework bug.
Translate the container's activation failure into CannotResolveDependency,
which names the member and parameter, preserves the original failure as the
inner exception, and appends a hint to call WithChronicle()/AddCratis() when
the failure involves a Chronicle type. Covers command Handle/Provide and
validators via ParameterDependencyResolver, and query performer dependencies.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Warn at compile time when a project sets up Arc with AddCratisArc but never calls WithChronicle() or AddCratis(), yet uses Chronicle in the same project: an aggregate root, reactor, reducer, projection, [EventType] event, or a type injecting IEventLog/IEventStore. Running Arc without Chronicle stays a valid, supported setup, so the rule is silent unless Chronicle is actually used, and it reports only within a single compilation so multi-project host setups never false-positive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expand the XML docs on AddCratis, UseCratis, AddCratisArc, UseCratisArc and WithChronicle across their overloads: clarify that the app hosts the Chronicle client rather than the engine, how the pieces compose, and the isolated setups. Add runnable <example> blocks to the AddCratis, AddCratisArc and WithChronicle decision points so the wiring shows up in IntelliSense. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clarify in the Cratis package guide that AddCratis wires the Chronicle client against a separately running instance (the cratis/chronicle container) rather than an embedded engine, and add a "Running Arc or Chronicle on their own" section. Add the ARCCHR0005 rule page and wire it into the code-analysis nav. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment on lines
+105
to
+116
| foreach (var type in GetAllTypes(compilation.Assembly.GlobalNamespace)) | ||
| { | ||
| if (IsAggregateRoot(type) || | ||
| ImplementsMarker(type, ReactorInterfaceName, ReactorsNamespace) || | ||
| ImplementsMarker(type, ReducerInterfaceName, ReducersNamespace) || | ||
| HasEventTypeAttribute(type, eventTypeAttribute) || | ||
| IsProjection(type, projectionForType) || | ||
| InjectsChronicleService(type)) | ||
| { | ||
| return type.Name; | ||
| } | ||
| } |
|
NuGet packages for this PR, e.g. Cratis.Arc: |
The message ended with "See the inner exception for details", which is a dead end once the exception is flattened into CommandResult.ExceptionMessages (strings only, no inner exception). Inline the underlying failure message so the actionable detail survives flattening. This keeps the .NET activation guidance visible for a missing validator dependency, which a Chronicle.Specs command scenario asserts on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Broaden the ARCCHR0005 analyzer from a fixed set of markers to any interface, attribute (on a type or its properties), or injected service interface in the Cratis.Chronicle namespace. This adds model-bound projections ([FromEvent], [SetFrom], [SetValue], ...) and covers new Chronicle features automatically, while aggregate roots (a Cratis.Arc.Chronicle base type) stay matched directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A reader interpreted "What AddCratis sets up for you" as running Chronicle in-process. Lead the bullet with "a client, not the engine", add a two-process topology diagram (app + Chronicle client -> separate Chronicle server), and correct the earlier "event store" wording to "event store client" so the deployment model is unmistakable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Setting up Arc without wiring Chronicle is a valid choice (commands and queries can run over MongoDB or EF Core). But if you do use Chronicle, forgetting
WithChronicle()used to surface as a cryptic container error at first use. This PR makes that mistake obvious across three layers — a compile-time analyzer, an actionable runtime error, and clearer docs and API reference.Added
ARCCHR0005analyzer that warns at compile time when a project sets up Arc withAddCratisArcbut never callsWithChronicle()orAddCratis(), yet uses Chronicle in the same project (an aggregate root, reactor, reducer, projection,[EventType]event, or a type injectingIEventLog/IEventStore). It stays silent when Chronicle genuinely is not used.Changed
IEventLogwithout Chronicle configured now throws an actionable error that names the command, query, or validator and its parameter, and points at the fix (WithChronicle()orAddCratis()) instead of a raw "Unable to resolve service for typeEventStoreName" container exception.AddCratis,UseCratis,AddCratisArc,UseCratisArc, andWithChronicle(all overloads): they clarify that the application hosts the Chronicle client — which connects to a separately running Chronicle instance — rather than an embedded engine, and include runnable examples for the common setups.