Skip to content

Commit 7751111

Browse files
authored
feat(application): add activity tracker pattern
Adds the Activity Tracker pattern with fluent and source-generated factory paths, TinyBDD coverage, DI-ready dashboard examples, docs, catalog coverage, and measured benchmarks.
1 parent 23a69f8 commit 7751111

25 files changed

Lines changed: 754 additions & 14 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ PatternKit currently tracks 92 production-readiness patterns. Each catalog patte
450450

451451
| Category | Count | Patterns |
452452
| --- | ---: | --- |
453-
| Application Architecture | 15 | Anti-Corruption Layer, Audit Log, CQRS, Data Mapper, Domain Event, Event Sourcing, Feature Toggle, Identity Map, Materialized View, Repository, Service Layer, Specification, Table Data Gateway, Transaction Script, Unit of Work |
453+
| Application Architecture | 16 | Activity Tracker, Anti-Corruption Layer, Audit Log, CQRS, Data Mapper, Domain Event, Event Sourcing, Feature Toggle, Identity Map, Materialized View, Repository, Service Layer, Specification, Table Data Gateway, Transaction Script, Unit of Work |
454454
| Behavioral | 11 | Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor |
455455
| Cloud Architecture | 17 | Ambassador, Backends for Frontends, Bulkhead, Cache-Aside, Circuit Breaker, External Configuration Store, Gateway Aggregation, Gateway Routing, Health Endpoint Monitoring, Leader Election, Priority Queue, Queue-Based Load Leveling, Rate Limiting, Retry, Scheduler Agent Supervisor, Sidecar, Strangler Fig |
456456
| Creational | 5 | Abstract Factory, Builder, Factory Method, Prototype, Singleton |
@@ -468,6 +468,8 @@ BenchmarkDotNet guidance is documented in [docs/guides/benchmarks.md](docs/guide
468468
| Abstract Factory | Execution | 750.189 ns | 6,200 B | 735.733 ns | 6,200 B | Same allocation; generated was slightly faster for login widget creation. |
469469
| Adapter | Construction | 34.668 ns | 320 B | 3.607 ns | 24 B | Generated adapter construction was materially faster and allocated less. |
470470
| Adapter | Execution | 59.084 ns | 416 B | 20.479 ns | 80 B | Generated adapter execution was faster and allocated less for shipment adaptation. |
471+
| Activity Tracker | Construction | 13.09 ns | 152 B | 12.98 ns | 152 B | Same allocation; generated was slightly faster in this microbenchmark. |
472+
| Activity Tracker | Execution | 446.88 ns | 1,656 B | 452.36 ns | 1,656 B | Same allocation; fluent was slightly faster for dashboard loading gates. |
471473
| Aggregator | Construction | 14.562 ns | 168 B | 15.235 ns | 168 B | Same allocation; fluent was slightly faster in this microbenchmark. |
472474
| Aggregator | Execution | 188.000 ns | 1,088 B | 200.564 ns | 1,088 B | Same allocation; fluent was faster for order line aggregation. |
473475
| Ambassador | Construction | 55.42 ns | 448 B | 48.03 ns | 360 B | Generated reduced construction time and allocation in this microbenchmark. |
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using BenchmarkDotNet.Attributes;
2+
using PatternKit.Application.ActivityTracking;
3+
using PatternKit.Examples.ActivityTrackingDemo;
4+
5+
namespace PatternKit.Benchmarks.Application;
6+
7+
[BenchmarkCategory("ApplicationArchitecture", "ActivityTracker")]
8+
public class ActivityTrackerBenchmarks
9+
{
10+
private static readonly DashboardLoadRequest Request = new("REQ-100", ["orders", "inventory", "pricing"]);
11+
12+
[Benchmark(Baseline = true, Description = "Fluent: create activity tracker")]
13+
[BenchmarkCategory("Fluent", "Construction")]
14+
public ActivityTracker Fluent_CreateActivityTracker()
15+
=> DashboardActivityTrackers.CreateFluent();
16+
17+
[Benchmark(Description = "Generated: create activity tracker")]
18+
[BenchmarkCategory("Generated", "Construction")]
19+
public ActivityTracker Generated_CreateActivityTracker()
20+
=> GeneratedDashboardActivityTracker.CreateGenerated();
21+
22+
[Benchmark(Description = "Fluent: track dashboard loading")]
23+
[BenchmarkCategory("Fluent", "Execution")]
24+
public DashboardLoadSummary Fluent_TrackDashboardLoading()
25+
=> DashboardActivityTrackerDemoRunner.RunFluent(Request);
26+
27+
[Benchmark(Description = "Generated: track dashboard loading")]
28+
[BenchmarkCategory("Generated", "Execution")]
29+
public DashboardLoadSummary Generated_TrackDashboardLoading()
30+
=> DashboardActivityTrackerDemoRunner.RunGeneratedStatic(Request);
31+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Dashboard Activity Tracker Example
2+
3+
The dashboard activity tracker example shows a loading-indicator gate composed through standard dependency injection:
4+
5+
- fluent `DashboardActivityTrackers.CreateFluent()` construction;
6+
- source-generated `GeneratedDashboardActivityTracker.CreateGenerated()` construction;
7+
- `AddDashboardActivityTrackerDemo()` for `IServiceCollection` integration;
8+
- TinyBDD coverage for fluent behavior, generated parity, direct DI registration, and aggregate `AddPatternKitExamples()` import.
9+
10+
```csharp
11+
var services = new ServiceCollection();
12+
services.AddDashboardActivityTrackerDemo();
13+
14+
using var provider = services.BuildServiceProvider(validateScopes: true);
15+
var runner = provider.GetRequiredService<DashboardActivityTrackerDemoRunner>();
16+
var summary = runner.RunGenerated(new DashboardLoadRequest("REQ-100", ["orders", "inventory"]));
17+
```
18+
19+
The summary reports whether the loading indicator should be visible, how many widget loads are active, and which widgets currently block dependent UI state.

docs/examples/toc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
- name: Production-Ready Example Integrations
55
href: production-ready-integrations.md
66

7+
- name: Dashboard Activity Tracker
8+
href: dashboard-activity-tracker.md
9+
710
- name: Auth & Logging with `ActionChain<HttpRequest>`
811
href: auth-logging-chain.md
912

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Activity Tracker Generator
2+
3+
`[GenerateActivityTracker]` emits a named `ActivityTracker` factory for applications that want tracker gates declared at compile time.
4+
5+
```csharp
6+
[GenerateActivityTracker(FactoryMethodName = "CreateGenerated", TrackerName = "dashboard-loading")]
7+
public static partial class GeneratedDashboardActivityTracker;
8+
```
9+
10+
The generated factory returns the normal runtime tracker:
11+
12+
```csharp
13+
var tracker = GeneratedDashboardActivityTracker.CreateGenerated();
14+
using var lease = tracker.Track("inventory", "REQ-100");
15+
```
16+
17+
Diagnostics:
18+
19+
| ID | Meaning |
20+
| --- | --- |
21+
| `PKAT001` | The host type must be partial. |
22+
| `PKAT002` | `FactoryMethodName` and `TrackerName` must be non-empty. |

docs/generators/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ PatternKit includes a Roslyn incremental generator package (`PatternKit.Generato
6161
| [**Specification**](specification.md) | Named business-rule registries | `[GenerateSpecificationRegistry]` |
6262
| [**Repository**](repository.md) | In-memory repository factories from key selectors | `[GenerateRepository]` |
6363
| [**Anti-Corruption Layer**](anti-corruption-layer.md) | External-to-domain translation boundaries with validation | `[GenerateAntiCorruptionLayer]` |
64+
| [**Activity Tracker**](activity-tracker.md) | Active-work tracker gates for loading and readiness workflows | `[GenerateActivityTracker]` |
6465
| [**Audit Log**](audit-log.md) | Append-only audit log factories from key selectors | `[GenerateAuditLog]` |
6566
| [**Unit of Work**](unit-of-work.md) | Ordered commit and rollback units | `[GenerateUnitOfWork]` |
6667
| [**Data Mapper**](data-mapper.md) | Domain/data model mapper factories | `[GenerateDataMapper]` |

docs/generators/toc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
- name: Anti-Corruption Layer
1111
href: anti-corruption-layer.md
1212

13+
- name: Activity Tracker
14+
href: activity-tracker.md
15+
1316
- name: Audit Log
1417
href: audit-log.md
1518

docs/guides/benchmark-results.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149
1515
| Abstract Factory | Execution | 750.189 ns | 6,200 B | 735.733 ns | 6,200 B | Same allocation; generated was slightly faster for login widget creation. |
1616
| Adapter | Construction | 34.668 ns | 320 B | 3.607 ns | 24 B | Generated adapter construction was materially faster and allocated less. |
1717
| Adapter | Execution | 59.084 ns | 416 B | 20.479 ns | 80 B | Generated adapter execution was faster and allocated less for shipment adaptation. |
18+
| Activity Tracker | Construction | 13.09 ns | 152 B | 12.98 ns | 152 B | Same allocation; generated was slightly faster in this microbenchmark. |
19+
| Activity Tracker | Execution | 446.88 ns | 1,656 B | 452.36 ns | 1,656 B | Same allocation; fluent was slightly faster for dashboard loading gates. |
1820
| Aggregator | Construction | 14.562 ns | 168 B | 15.235 ns | 168 B | Same allocation; fluent was slightly faster in this microbenchmark. |
1921
| Aggregator | Execution | 188.000 ns | 1,088 B | 200.564 ns | 1,088 B | Same allocation; fluent was faster for order line aggregation. |
2022
| Ambassador | Construction | 55.42 ns | 448 B | 48.03 ns | 360 B | Generated reduced construction time and allocation in this microbenchmark. |
@@ -204,25 +206,26 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149
204206

205207
## Coverage Matrix Summary
206208

207-
The coverage matrix currently publishes 93 catalog patterns and 372 pattern route results. Each pattern has four BenchmarkDotNet routes: fluent construction, fluent execution, source-generated construction, and source-generated execution.
209+
The coverage matrix currently publishes 94 catalog patterns and 376 pattern route results. Each pattern has four BenchmarkDotNet routes: fluent construction, fluent execution, source-generated construction, and source-generated execution.
208210

209211
| Category | Patterns | Published route results |
210212
| --- | ---: | ---: |
211-
| Application Architecture | 15 | 60 |
213+
| Application Architecture | 16 | 64 |
212214
| Behavioral | 11 | 44 |
213215
| Cloud Architecture | 17 | 68 |
214216
| Creational | 5 | 20 |
215217
| Enterprise Integration | 34 | 136 |
216218
| Messaging Reliability | 3 | 12 |
217219
| Structural | 7 | 28 |
218220

219-
The generator matrix currently publishes 89 generator source route results.
221+
The generator matrix currently publishes 90 generator source route results.
220222

221223
## Pattern Matrix Results
222224

223-
| Category | Pattern | Fluent construction | Fluent execution | Generated construction | Generated execution |
224-
| --- | --- | --- | --- | --- | --- |
225-
| Application Architecture | Anti-Corruption Layer | Covered | Covered | Covered | Covered |
225+
| Category | Pattern | Fluent construction | Fluent execution | Generated construction | Generated execution |
226+
| --- | --- | --- | --- | --- | --- |
227+
| Application Architecture | Activity Tracker | Covered | Covered | Covered | Covered |
228+
| Application Architecture | Anti-Corruption Layer | Covered | Covered | Covered | Covered |
226229
| Application Architecture | Audit Log | Covered | Covered | Covered | Covered |
227230
| Application Architecture | CQRS | Covered | Covered | Covered | Covered |
228231
| Application Architecture | Data Mapper | Covered | Covered | Covered | Covered |
@@ -318,9 +321,10 @@ The generator matrix currently publishes 89 generator source route results.
318321

319322
## Generator Matrix Results
320323

321-
| Generator | Source | Matrix result |
322-
| --- | --- | --- |
323-
| AdapterGenerator | `src/PatternKit.Generators/Adapter/AdapterGenerator.cs` | Covered |
324+
| Generator | Source | Matrix result |
325+
| --- | --- | --- |
326+
| ActivityTrackerGenerator | `src/PatternKit.Generators/ActivityTracking/ActivityTrackerGenerator.cs` | Covered |
327+
| AdapterGenerator | `src/PatternKit.Generators/Adapter/AdapterGenerator.cs` | Covered |
324328
| AmbassadorGenerator | `src/PatternKit.Generators/Ambassador/AmbassadorGenerator.cs` | Covered |
325329
| AntiCorruptionLayerGenerator | `src/PatternKit.Generators/AntiCorruption/AntiCorruptionLayerGenerator.cs` | Covered |
326330
| AuditLogGenerator | `src/PatternKit.Generators/AuditLog/AuditLogGenerator.cs` | Covered |

docs/guides/pattern-coverage.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr
113113
| Application Architecture | Audit Log | `IAuditLog<TEntry,TKey>` and `InMemoryAuditLog<TEntry,TKey>` | Audit Log generator |
114114
| Application Architecture | Materialized View | `IMaterializedView<TState,TEvent>` and `MaterializedView<TState,TEvent>` | Materialized View generator |
115115
| Application Architecture | Anti-Corruption Layer | `AntiCorruptionLayer<TExternal, TDomain>` | Anti-Corruption Layer generator |
116+
| Application Architecture | Activity Tracker | `ActivityTracker` | Activity Tracker generator |
116117

117118
## Research Baselines
118119

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Activity Tracker
2+
3+
`ActivityTracker` models tracker-based gating for active work. A caller acquires an `ActivityLease` when work begins, and releases it by disposing the lease or completing the activity id. Dependent state is based only on whether any activities exist.
4+
5+
```csharp
6+
var tracker = ActivityTracker.Create("dashboard-loading").Build();
7+
8+
using var loadOrders = tracker.Track("orders", correlationId: "REQ-100");
9+
10+
if (tracker.IsBlocked)
11+
{
12+
// Show the loading indicator or hold dependent work.
13+
}
14+
```
15+
16+
Use it for loading wheels, import gates, page readiness, background refresh coordination, and other workflows where work can enter or leave independently and the block state is `ActiveCount > 0`.
17+
18+
The source-generated path uses `[GenerateActivityTracker]` to produce a named tracker factory:
19+
20+
```csharp
21+
[GenerateActivityTracker(FactoryMethodName = "CreateGenerated", TrackerName = "dashboard-loading")]
22+
public static partial class GeneratedDashboardActivityTracker;
23+
```
24+
25+
`DashboardActivityTrackerDemo` shows a production-oriented `IServiceCollection` registration that drives dashboard loading visibility from tracked widget loads. Import it with `AddDashboardActivityTrackerDemo()` or the aggregate `AddPatternKitExamples()` registration.

0 commit comments

Comments
 (0)