Skip to content

Commit 40bb202

Browse files
committed
doc: add Fusion-Cheat-Sheet.md
1 parent 6bfbd59 commit 40bb202

File tree

2 files changed

+226
-4
lines changed

2 files changed

+226
-4
lines changed

docs/tutorial/Fusion-Cheat-Sheet.md

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
# Fusion Cheat Sheet
2+
3+
## Compute Services
4+
5+
Compute service interface:
6+
```cs
7+
// IComputeService is just an optional tagging interface.
8+
// Nevertheless, we highly recommend to "implement" it -
9+
// it allows you to use a few extension methods, such as
10+
// .GetServices() and .GetCommander().
11+
public interface ICartService : IComputeService
12+
{
13+
// When applied to interface method, this attribute
14+
// is "inherited" by the implementation
15+
[ComputeMethod]
16+
Task<List<Order>> GetOrders(long cartId, CancellationToken cancellationToken = default);
17+
}
18+
```
19+
Compute service implementation:
20+
```cs
21+
public class CartService : ICartService
22+
{
23+
// The method must be virtual + return Task<T>
24+
public virtual async Task<List<Order>> GetOrders(long cartId, CancellationToken cancellationToken)
25+
{
26+
// ...
27+
}
28+
}
29+
```
30+
31+
Add invalidation logic (after any code that changes the result of what's invalidated inside the `using` block below):
32+
```cs
33+
using (Computed.Invalidate()) {
34+
// Whatever compute method calls you make inside this block
35+
// are invalidating calls. Instead of running the actual method
36+
// code they invalidate the result of this call.
37+
// They always complete synchronously, and you can pass
38+
// "default" instead of any CancellationToken here.
39+
_ = GetOrders(cartId, default);
40+
}
41+
```
42+
43+
Register compute service:
44+
```cs
45+
fusion.AddComputeService<IOrderService, OrderService>();
46+
```
47+
48+
## Replica Services
49+
50+
Add controller:
51+
```cs
52+
[Route("api/[controller]/[action]")]
53+
[ApiController, JsonifyErrors, UseDefaultSession]
54+
public class CartController : ControllerBase, ICartService
55+
{
56+
private readonly ICartService _cartService;
57+
private readonly ICommander _commander;
58+
59+
public CartController(ICartService service, ICommander commander)
60+
{
61+
_service = service;
62+
_commander = commander;
63+
}
64+
65+
[HttpGet, Publish]
66+
public Task<List<Order>> GetOrders(long cartId, CancellationToken cancellationToken)
67+
=> _service.GetOrders(cartId, cancellationToken);
68+
}
69+
```
70+
71+
Add client definition:
72+
```cs
73+
[BasePath("cart")]
74+
public interface ICartClientDef
75+
{
76+
[Get(nameof(GetOrders))]
77+
Task<List<Order>> GetOrders(long cartId, CancellationToken cancellationToken);
78+
}
79+
```
80+
81+
Configure Fusion client (this has to be done once in a code that configures client-side `IServiceProvider`):
82+
```cs
83+
var baseUri = new Uri("http://localhost:5005");
84+
var apiBaseUri = new Uri($"{baseUri}api/");
85+
86+
var fusion = services.AddFusion();
87+
fusion.AddRestEaseClient(
88+
client => {
89+
client.ConfigureWebSocketChannel(_ => new() { BaseUri = baseUri });
90+
client.ConfigureHttpClient((_, name, o) => {
91+
var isFusionClient = (name ?? "").StartsWith("Stl.Fusion");
92+
var clientBaseUri = isFusionClient ? baseUri : apiBaseUri;
93+
o.HttpClientActions.Add(httpClient => httpClient.BaseAddress = clientBaseUri);
94+
});
95+
client.AddReplicaService<ITodoService, ITodoClientDef>();
96+
});
97+
```
98+
99+
Register Replica Service:
100+
```cs
101+
// After "var fusionClient = ..."
102+
fusionClient.AddComputeService<IOrderService, OrderService>();
103+
```
104+
105+
Use Replica Service:
106+
```cs
107+
// Just call it the same way you call the original one.
108+
// Any calls that are expected to produce the same result
109+
// as the previous call with the same arguments will
110+
// be resolved via locally cached IComputed.
111+
// When a IComputed gets invalidated on the server,
112+
// Fusion will invalidate its replica on every client.
113+
```
114+
115+
## Commander
116+
117+
Declare command type:
118+
```cs
119+
// You don't have to use records, but we recommend to use
120+
// immutable types for commands and outputs of compute methods
121+
public record UpdateCartCommand(long CartId, Dictionary<long, long?> Updates)
122+
: ICommand<Unit> // Unit is command's return type; you can use any other
123+
{
124+
// Compatibility: Newtonsoft.Json needs this constructor to deserialize the record
125+
public UpdateCartCommand() : this(0, null!) { }
126+
}
127+
```
128+
129+
Add command handler in the compute service interface:
130+
```cs
131+
public interface ICartService : IComputeService
132+
{
133+
// ...
134+
[CommandHandler] // This attribute is also "inherited" by the impl.
135+
Task<Unit> UpdateCart(UpdateCartCommand command, CancellationToken cancellationToken = default);
136+
```
137+
138+
Add command handler implementation:
139+
```cs
140+
public class CartService : ICartService
141+
{
142+
// Must be virtual + return Task<T> for ICommand<T>;
143+
// Command must be its first argument; other arguments are resolved
144+
// from DI container - except CancellationToken, which is
145+
// passed directly.
146+
public virtual Task<Unit> UpdateCart(UpdateCartCommand command, CancellationToken cancellationToken)
147+
{
148+
if (Computed.IsInvalidating()) {
149+
// Write the invalidation logic for this command here.
150+
//
151+
// A set of command handlers registered by Fusion will
152+
// "retry" this handler inside the invalidation block
153+
// once its "normal" logic completes successfully.
154+
// Moreover, they'll run this block on every node on the
155+
// cluster if you use multi-host invalidation.
156+
return default;
157+
}
158+
159+
// Command handler code goes here
160+
}
161+
162+
```
163+
164+
Register command handler:
165+
```
166+
// Nothing is needed for handlers declared inside compute services
167+
```
168+
169+
## Exposing commands to the client via Replica Services
170+
171+
Add controller method for the command:
172+
```cs
173+
public class CartController : ControllerBase, ICartService
174+
// Always use [HttpPost] and [FromBody]
175+
[HttpPost]
176+
public Task<Unit> UpdateCart([FromBody] UpdateCartCommand command, CancellationToken cancellationToken)
177+
// You can also call corresponding service method directly here,
178+
// but if you're using CommanderOptions.AllowDirectCommandHandlerCalls = false,
179+
// only this "style" of command invocation will work.
180+
// For the note, they work the same, i.e. when a command handler
181+
// is invoked directly, the call is still routed via ICommander.
182+
=> _commander.Call(command, cancellationToken);
183+
```
184+
185+
Add command handler in the client definition interface:
186+
```cs
187+
public interface ICartClientDef
188+
{
189+
// Always use [Post] and [Body] here
190+
[Post(nameof(UpdateCart))]
191+
Task<Unit> UpdateCart([Body] UpdateCartCommand command, CancellationToken cancellationToken);
192+
```
193+
194+
Register client-side command handler:
195+
```
196+
// Nothing is needed for handlers declared inside replica services
197+
```
198+
199+
## Working with `IComputed`
200+
201+
Capture:
202+
```cs
203+
var computed = await Computed.Capture(ct => service.ComputeMethod(args, ct), cancellationToken);
204+
```
205+
206+
Check its state:
207+
```cs
208+
if (computed.IsConsistent()) {
209+
// ...
210+
}
211+
```
212+
213+
Await for invalidation:
214+
```cs
215+
await computed.WhenInvalidated(cancellationToken);
216+
// Or
217+
computed.Invalidated += c => Console.WriteLine("Invalidated!");
218+
```
219+
220+
To be continued.
221+
222+
#### [&gt; Back to Tutorial](./README.md) | [Documentation Home](../index.md)

docs/tutorial/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ Without further ado:
5555
* [Part 10: Multi-Host Invalidation and CQRS with Operations Framework](./Part10.md)
5656
* <img src="https://img.shields.io/badge/-New!-brightgreen" valign="middle"> [Part 11: Authentication in Fusion](./Part11.md)
5757
* [Epilogue](./PartFF.md)
58-
59-
Check out the
60-
[Overview](https://github.com/servicetitan/Stl.Fusion/blob/master/docs/Overview.md)
61-
as well - it provides a high-level description of Fusion abstractions.
58+
59+
Finally, check out:
60+
- <img src="https://img.shields.io/badge/-New!-brightgreen" valign="middle"> [Fusion Cheat Sheet](./Fusion-Cheat-Sheet.md) - consider adding it to Favorites :)
61+
- [Overview](https://github.com/servicetitan/Stl.Fusion/blob/master/docs/Overview.md) - a high-level description of Fusion abstractions.
6262

6363
Join our [Discord Server] to ask questions and track project updates.
6464

0 commit comments

Comments
 (0)