Skip to content

Commit 19031b7

Browse files
Projection and query for #31 to illustrate alternatives to a transaction
1 parent 09d226d commit 19031b7

File tree

6 files changed

+252
-7
lines changed

6 files changed

+252
-7
lines changed

examples/bank-account/RetailBank.AzureFunctionApp/RetailBank.AzureFunctionApp.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,4 @@
5656
<Private>true</Private>
5757
</ProjectReference>
5858
</ItemGroup>
59-
<ItemGroup>
60-
<Folder Include="Transfer\Functions\" />
61-
<Folder Include="Transfer\Projections\" />
62-
</ItemGroup>
6359
</Project>

examples/bank-account/RetailBank.AzureFunctionApp/Transfer/Events/TargetFundsDeposited.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using EventSourcingOnAzureFunctions.Common.EventSourcing;
2-
using System;
3-
using System.Collections.Generic;
4-
using System.Text;
52

63
namespace RetailBank.AzureFunctionApp.Transfer.Events
74
{
5+
/// <summary>
6+
/// Funds being transfered have been deposited into the target account
7+
/// </summary>
88
[EventName("Target Funds Deposited")]
99
public sealed class TargetFundsDeposited
1010
{
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace RetailBank.AzureFunctionApp.Transfer.Functions
6+
{
7+
/// <summary>
8+
/// Data used to initiate a "money transfer" request
9+
/// </summary>
10+
public sealed class MoneyTransferData
11+
{
12+
13+
/// <summary>
14+
/// The amount we want to transfer
15+
/// </summary>
16+
public decimal Amount { get; set; }
17+
18+
/// <summary>
19+
/// The account that the transfer will come from
20+
/// </summary>
21+
public string SourceAccountNumber { get; set; }
22+
23+
/// <summary>
24+
/// The account which the transfer will be paid into
25+
/// </summary>
26+
public string TargetAccountNumber { get; set; }
27+
28+
/// <summary>
29+
/// Extra text attached to the transfer
30+
/// </summary>
31+
public string Commentary { get; set; }
32+
}
33+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using EventSourcingOnAzureFunctions.Common.Binding;
2+
using EventSourcingOnAzureFunctions.Common.CQRS;
3+
using EventSourcingOnAzureFunctions.Common.EventSourcing;
4+
5+
namespace RetailBank.AzureFunctionApp.Transfer.Functions
6+
{
7+
public class TransferCommands
8+
{
9+
10+
11+
12+
}
13+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using EventSourcingOnAzureFunctions.Common.Binding;
2+
using EventSourcingOnAzureFunctions.Common.EventSourcing;
3+
using Microsoft.Azure.WebJobs;
4+
using Microsoft.Azure.WebJobs.Extensions.Http;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Net.Http;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
using RetailBank.AzureFunctionApp.Transfer.Projections;
11+
using System.Diagnostics;
12+
13+
namespace RetailBank.AzureFunctionApp.Transfer.Functions
14+
{
15+
16+
/// <summary>
17+
/// Queries to execute against the money transfer entity
18+
/// </summary>
19+
public sealed class TransferQueries
20+
{
21+
22+
// GetTransferState
23+
[FunctionName("GetTransferState")]
24+
public static async Task<HttpResponseMessage> GetTransferStateRun(
25+
[HttpTrigger(AuthorizationLevel.Function, "GET", Route = @"GetTransferState/{transfernumber}")] HttpRequestMessage req,
26+
string transfernumber,
27+
[Projection("Bank", "Transfer", "{transfernumber}", nameof(TransferState))] Projection prjTransferState)
28+
{
29+
30+
// Set the start time for how long it took to process the message
31+
DateTime startTime = DateTime.UtcNow;
32+
33+
#region Tracing telemetry
34+
Activity.Current.AddTag("Transfer Number", transfernumber);
35+
#endregion
36+
37+
string result = $"No fund transfer found with the identifier : {transfernumber}";
38+
39+
if (null != prjTransferState)
40+
{
41+
if (await prjTransferState.Exists())
42+
{
43+
// Run the "Transfer state" projection
44+
TransferState state = await prjTransferState.Process<TransferState >();
45+
46+
result = $"Transfer {transfernumber} state is {state.LastStateChange} ({state.AmountDeposited} of {state.AmountOfTransferRequested} transfered) ";
47+
return req.CreateResponse<ProjectionFunctionResponse>(System.Net.HttpStatusCode.OK,
48+
ProjectionFunctionResponse.CreateResponse(startTime,
49+
false,
50+
result,
51+
state.CurrentSequenceNumber),
52+
FunctionResponse.MEDIA_TYPE);
53+
54+
}
55+
else
56+
{
57+
// No such transfer request exists
58+
result = $"Transfer {transfernumber} is not yet created - cannot retrieve a state for it";
59+
return req.CreateResponse<ProjectionFunctionResponse>(System.Net.HttpStatusCode.NotFound,
60+
ProjectionFunctionResponse.CreateResponse(startTime,
61+
true,
62+
result,
63+
0),
64+
FunctionResponse.MEDIA_TYPE);
65+
}
66+
}
67+
68+
69+
// If we got here no transfer was found
70+
return req.CreateResponse<ProjectionFunctionResponse>(System.Net.HttpStatusCode.NotFound,
71+
ProjectionFunctionResponse.CreateResponse(startTime,
72+
true,
73+
result,
74+
0),
75+
FunctionResponse.MEDIA_TYPE);
76+
}
77+
78+
}
79+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using EventSourcingOnAzureFunctions.Common.EventSourcing;
2+
using System;
3+
using RetailBank.AzureFunctionApp.Transfer.Events;
4+
using EventSourcingOnAzureFunctions.Common.EventSourcing.Interfaces;
5+
6+
namespace RetailBank.AzureFunctionApp.Transfer.Projections
7+
{
8+
/// <summary>
9+
/// A projection to return the state of a given money transfer
10+
/// </summary>
11+
[ProjectionName("Money Transfer State")]
12+
public sealed class TransferState
13+
: ProjectionBase,
14+
IHandleEventType<TransferInitiated>,
15+
IHandleEventType<SourceFundsWithdrawn >,
16+
IHandleEventType<SourceFundsRefunded >,
17+
IHandleEventType<TargetFundsDeposited >,
18+
IHandleEventType<RefundInitiated >
19+
{
20+
21+
private string lastStateChange;
22+
/// <summary>
23+
/// The last state event that happened in this transfers life cycle
24+
/// </summary>
25+
public string LastStateChange
26+
{
27+
get
28+
{
29+
return lastStateChange;
30+
}
31+
}
32+
33+
private decimal amountOfTransferRequest;
34+
/// <summary>
35+
/// How much money transfer was initially requested
36+
/// </summary>
37+
public decimal AmountOfTransferRequested
38+
{
39+
get
40+
{
41+
return amountOfTransferRequest;
42+
}
43+
}
44+
45+
private decimal amountWithdrawn;
46+
/// <summary>
47+
/// The amount that has been withdrawn from the source account
48+
/// </summary>
49+
public decimal AmountWithdrawn
50+
{
51+
get
52+
{
53+
return amountWithdrawn;
54+
}
55+
}
56+
57+
private decimal amountToRefund;
58+
public decimal AmountToRefund
59+
{
60+
get
61+
{
62+
return amountToRefund;
63+
}
64+
}
65+
66+
67+
private decimal amountDeposited;
68+
/// <summary>
69+
/// The amound that has been deposited into the target account of the money transfer
70+
/// </summary>
71+
public decimal AmountDeposited
72+
{
73+
get
74+
{
75+
return amountDeposited;
76+
}
77+
}
78+
79+
public void HandleEventInstance(TransferInitiated eventInstance)
80+
{
81+
if (eventInstance != null)
82+
{
83+
lastStateChange = "Initiated";
84+
amountOfTransferRequest = eventInstance.Amount;
85+
}
86+
}
87+
88+
public void HandleEventInstance(SourceFundsWithdrawn eventInstance)
89+
{
90+
if (eventInstance != null)
91+
{
92+
lastStateChange = "Source Funds Withdrawn";
93+
amountWithdrawn += eventInstance.AmountWithdrawn;
94+
}
95+
}
96+
97+
public void HandleEventInstance(SourceFundsRefunded eventInstance)
98+
{
99+
if (eventInstance != null)
100+
{
101+
lastStateChange = "Source Funds Refunded";
102+
amountWithdrawn -= eventInstance.AmountRefunded;
103+
}
104+
}
105+
106+
public void HandleEventInstance(TargetFundsDeposited eventInstance)
107+
{
108+
if (eventInstance != null)
109+
{
110+
lastStateChange = "Target Funds Deposited";
111+
amountDeposited += eventInstance.AmountDeposited;
112+
}
113+
}
114+
115+
public void HandleEventInstance(RefundInitiated eventInstance)
116+
{
117+
if (eventInstance != null)
118+
{
119+
lastStateChange = "Refund Initiated";
120+
amountToRefund += eventInstance.AmountToRefund;
121+
}
122+
}
123+
}
124+
}

0 commit comments

Comments
 (0)