@@ -32,28 +32,27 @@ dotnet add package Zooper.Bee
3232
3333``` csharp
3434// Define a simple railway
35- var railway = new RailwayBuilder <Request , Payload , SuccessResult , ErrorResult >(
35+ var railway = Railway . Create <Request , Payload , SuccessResult , ErrorResult >(
3636 // Factory function that creates the initial payload from the request
37- request => new Payload { Data = request .Data },
37+ factory : request => new Payload { Data = request .Data },
3838
3939 // Selector function that creates the success result from the final payload
40- payload => new SuccessResult { ProcessedData = payload .Data }
41- )
42- .Validate (request =>
43- {
44- // Validate the request
45- if (string .IsNullOrEmpty (request .Data ))
46- return Option <ErrorResult >.Some (new ErrorResult { Message = " Data is required" });
40+ selector : payload => new SuccessResult { ProcessedData = payload .Data },
4741
48- return Option <ErrorResult >.None ;
49- })
50- .Do (payload =>
51- {
52- // Process the payload
53- payload .Data = payload .Data .ToUpper ();
54- return Either <ErrorResult , Payload >.FromRight (payload );
55- })
56- .Build ();
42+ // Step execution phase
43+ steps : s => s
44+ .Validate (request =>
45+ {
46+ if (string .IsNullOrEmpty (request .Data ))
47+ return Option <ErrorResult >.Some (new ErrorResult { Message = " Data is required" });
48+ return Option <ErrorResult >.None ;
49+ })
50+ .Do (payload =>
51+ {
52+ payload .Data = payload .Data .ToUpper ();
53+ return Either <ErrorResult , Payload >.FromRight (payload );
54+ })
55+ );
5756
5857// Execute the railway
5958var result = await railway .Execute (new Request { Data = " hello world" }, CancellationToken .None );
6968
7069## Building Railways
7170
71+ Railways are created with ` Railway.Create() ` , which takes two separate configuration lambdas:
72+
73+ - ** ` guards ` ** — optional; declares guards and validations that run before the payload is created
74+ - ** ` steps ` ** — required; declares all activities that transform the payload
75+
76+ This two-phase separation makes it structurally impossible to mix guard registration with step
77+ registration. ` Guard() ` and ` Validate() ` are not available inside ` steps ` , and ` Do() ` /` Group() ` /etc.
78+ are not available inside ` guards ` .
79+
80+ ``` csharp
81+ var railway = Railway .Create <Request , Payload , Success , Error >(
82+ factory : request => new Payload (request ),
83+ selector : payload => new Success (payload .Result ),
84+ guards : g => g
85+ .Guard (request => /* auth check */ )
86+ .Validate (request => /* input validation */ ),
87+ steps : s => s
88+ .Do (payload => /* step 1 */ )
89+ .Group (null , g => g
90+ .Do (payload => /* step 2a */ )
91+ .Do (payload => /* step 2b */ ))
92+ .Do (payload => /* step 3 */ )
93+ );
94+ ```
95+
96+ When no guards are needed, omit the ` guards ` parameter:
97+
98+ ``` csharp
99+ var railway = Railway .Create <Request , Payload , Success , Error >(
100+ factory : request => new Payload (request ),
101+ selector : payload => new Success (payload .Result ),
102+ steps : s => s
103+ .Do (payload => /* ... */ )
104+ );
105+ ```
106+
72107### Validation
73108
74- Validates the incoming request before processing begins .
109+ Validations run before any step and reject the request early when invalid .
75110
76111``` csharp
77112// Asynchronous validation
@@ -91,31 +126,36 @@ Validates the incoming request before processing begins.
91126
92127### Guards
93128
94- Guards allow you to define checks that run before a railway begins execution. They're ideal for authentication,
95- authorization, account validation, or any other requirement that must be satisfied before a railway can proceed.
129+ Guards check whether the railway is allowed to execute at all — authentication,
130+ authorization, feature flags, etc. They always run before any step, regardless of
131+ where they appear in the ` guards ` lambda.
96132
97133``` csharp
98- // Asynchronous guard
99- .Guard (async (request , cancellationToken ) =>
100- {
101- var isAuthorized = await CheckAuthorizationAsync (request , cancellationToken );
102- return isAuthorized ? Option <ErrorResult >.None : Option <ErrorResult >.Some (new ErrorResult ());
103- })
104-
105- // Synchronous guard
106- .Guard (request =>
107- {
108- var isAuthorized = CheckAuthorization (request );
109- return isAuthorized ? Option <ErrorResult >.None : Option <ErrorResult >.Some (new ErrorResult ());
110- })
134+ guards : g => g
135+ // Asynchronous guard
136+ .Guard (async (request , cancellationToken ) =>
137+ {
138+ var isAuthorized = await CheckAuthorizationAsync (request , cancellationToken );
139+ return isAuthorized
140+ ? Either <ErrorResult , Unit >.FromRight (Unit .Value )
141+ : Either <ErrorResult , Unit >.FromLeft (new ErrorResult { Message = " Unauthorized" });
142+ })
143+ // Synchronous guard
144+ .Guard (request =>
145+ {
146+ var isAuthorized = CheckAuthorization (request );
147+ return isAuthorized
148+ ? Either <ErrorResult , Unit >.FromRight (Unit .Value )
149+ : Either <ErrorResult , Unit >.FromLeft (new ErrorResult { Message = " Unauthorized" });
150+ })
111151```
112152
113153#### Benefits of Guards
114154
115- - Guards run before creating the railway context, providing early validation
116- - They provide a clear separation between "can this railway run?" and the actual railway logic
155+ - Guards run before the payload is created , providing the earliest possible short - circuit
156+ - The `guards ` phase is structurally separate from the `steps ` phase — it is impossible to
157+ accidentally register a guard after a step
117158- Common checks like authentication can be standardized and reused
118- - Failures short-circuit the railway, preventing unnecessary work
119159
120160### Activities
121161
@@ -349,6 +389,38 @@ services.AddRailways(lifetime: ServiceLifetime.Singleton);
3493896 . Validate requests early to fail fast
3503907 . Use contextual state to avoid passing too many parameters
351391
392+ ## Migration from ` RailwayBuilder ` to ` Railway.Create() `
393+
394+ As of the latest version, ` RailwayBuilder ` and ` RailwayBuilderFactory ` are ` [Obsolete] ` .
395+ Use ` Railway.Create() ` instead.
396+
397+ ### Before
398+
399+ ``` csharp
400+ var railway = new RailwayBuilder <Request , Payload , Success , Error >(
401+ request => new Payload (request ),
402+ payload => new Success (payload .Result ))
403+ .Guard (request => /* ... */ )
404+ .Validate (request => /* ... */ )
405+ .Do (payload => /* ... */ )
406+ .Group (null , g => g .Do (payload => /* ... */ ))
407+ .Build ();
408+ ```
409+
410+ ### After
411+
412+ ``` csharp
413+ var railway = Railway .Create <Request , Payload , Success , Error >(
414+ factory : request => new Payload (request ),
415+ selector : payload => new Success (payload .Result ),
416+ guards : g => g
417+ .Guard (request => /* ... */ )
418+ .Validate (request => /* ... */ ),
419+ steps : s => s
420+ .Do (payload => /* ... */ )
421+ .Group (null , g => g .Do (payload => /* ... */ )));
422+ ```
423+
352424## Migration from Workflow to Railway
353425
354426As of the latest version, all ` Workflow ` classes have been renamed to ` Railway ` to better reflect the railway-oriented programming pattern used by the library. The old ` Workflow ` names are preserved as ` [Obsolete] ` shims for backward compatibility.
0 commit comments