|
Identity groups need some way of evaluating if any given identity is in or out of the group based on the event stream of that entity.
This is somewhat similar to how a projection works but I think to avoid confusion I shall call this specific case of a projection a "classifier".
(I'm probably going to rely on the magic of inheritance to make the code underlying this and the code underlying a projection match)
|
|
|
|
|
Classifiers
A classifier is a special form of a projection which runs over the event stream of an aggregate and decides if it is "in" or "out" of the identity group as at any given point in time.
In the above example of the "accounts held by US citizens" identity group the classifier might run over events occurring to the aggregate identifier "account" and decide if the account is in or out of the group based on "account opened", "beneficial ownership changed" and any other similar events.
|
|
|
|
|
Have the very early cut of the code generation from the CQRS DSL designer working now...
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict Off
Option Explicit On
Imports CQRSAzure
Imports CQRSAzure.Aggregation
Imports CQRSAzure.EventSourcing
Namespace Football_League.Player.eventDefinition
'''<summary>
'''Player registered with the league
'''</summary>
'''<remarks>
'''This will generate the player's unique regsitration id
'''</remarks>
Partial Public Class Registered
Inherits Object
Implements IRegistered
#Region "Private members"
Private _PreviousClub As Date
Private _Amateur As Boolean
Private _DateOfBirth As Date
#End Region
'''<summary>
'''Empty constructor for serialisation
'''This should be removed if serialisation is not needed
'''</summary>
Sub New()
MyBase.New
End Sub
'''<summary>
'''Create and populate a new instance of this class from the underlying interface
'''</summary>
'''<remarks>
'''This should be called when the event is created from an event stream
'''</remarks>
Sub New(ByVal RegisteredInit As IRegistered)
MyBase.New
_PreviousClub = RegisteredInit.PreviousClub
_Amateur = RegisteredInit.Amateur
_DateOfBirth = RegisteredInit.DateOfBirth
End Sub
'''<summary>
'''Create and populate a new instance of this class from the underlying properties
'''</summary>
'''<remarks>
'''This should be called when the event is created from an event stream
'''</remarks>
Sub New(ByVal PreviousClub_In As Date, ByVal Amateur_In As Boolean, ByVal DateOfBirth_In As Date)
MyBase.New
_PreviousClub = PreviousClub_In
_Amateur = Amateur_In
_DateOfBirth = DateOfBirth_In
End Sub
'''<summary>
'''Where did the player come from
'''</summary>
Public ReadOnly Property PreviousClub() As Date
Get
Return _PreviousClub
End Get
End Property
'''<summary>
'''Is the player registered as an amateur
'''</summary>
Public ReadOnly Property Amateur() As Boolean
Get
Return _Amateur
End Get
End Property
'''<summary>
'''Player's date of birth
'''</summary>
Public ReadOnly Property DateOfBirth() As Date
Get
Return _DateOfBirth
End Get
End Property
End Class
End Namespace
There's a long way to go from here - but this is definitely heading in the right direction!
|
|
|
|
|
although - code generation does result in some ugly variable names
'''<summary>
'''Create and populate a new instance of this class from the underlying properties
'''</summary>
'''<remarks>
'''This should be called when the event is created from an event stream
'''</remarks>
Sub New(ByVal Start_In As Date, ByVal Court_In As Integer, ByVal Umpire_In As String, ByVal Player_s__1_In As String, ByVal Player_s__2_In As String)
MyBase.New
_Start = Start_In
_Court = Court_In
_Umpire = Umpire_In
_Player_s__1 = Player_s__1_In
_Player_s__2 = Player_s__2_In
End Sub
|
|
|
|
|
More code generation - this time for factory methods...
Shared Function Create(ByVal Date_Of_Birth_In As Date,
ByVal Observations_In As String,
ByVal Heiffer_Identifier_In As String) As IBorn
Return New Born(Date_Of_Birth_In, Observations_In, Heiffer_Identifier_In)
End Function
|
|
|
|
|
And code generation for projections....
Option Strict Off
Option Explicit On
Imports CQRSAzure
Imports CQRSAzure.Aggregation
Imports CQRSAzure.EventSourcing
Imports Herd.Cow
Imports Herd.Cow.eventDefinition
Namespace Herd.Cow.projection
Partial Public Class Location
Inherits Object
Implements ILocation
#Region "Private members"
Private _In_Shed As Boolean
Private _Location As String
#End Region
Public ReadOnly Property In_Shed() As Boolean Implements ILocation.In_Shed
Get
Return _In_Shed
End Get
End Property
Public ReadOnly Property Location() As String Implements ILocation.Location
Get
Return _Location
End Get
End Property
Public Overloads Sub HandleEvent(ByVal eventToHandle As IMoved_To_Field) Implements CQRSAzure.EventSourcing.IHandleEvent(Of IMoved_To_Field).HandleEvent
_In_Shed = False
_Location = eventToHandle.Moved_To
End Sub
Public Overloads Sub HandleEvent(ByVal eventToHandle As IMoved_To_Shed) Implements CQRSAzure.EventSourcing.IHandleEvent(Of IMoved_To_Shed).HandleEvent
_In_Shed = True
_Location = eventToHandle.Shed_Name
End Sub
End Class
End Namespace
|
|
|
|
|
For a query definition, two classes are created. The "Definition" class is in charge of specifying the model for the input parameters and return parameters (as would be used in the MVVM / MVC front end):-
Imports CQRSAzure
Imports CQRSAzure.Aggregation
Imports CQRSAzure.EventSourcing
Imports CQRSAzure.QueryDefinition
Imports Herd.Cow
Namespace Herd.Cow.queryDefinition
Partial Public Class Get_Herd_Location_Definition
Inherits QueryDefinitionBase(Of IEnumerable(Of IGet_Herd_Location_Definition_Return))
Implements IGet_Herd_Location_Definition
Private Property As_Of_Date() As Date Implements IGet_Herd_Location_Definition.As_Of_Date
Get
Return MyBase.GetParameterValue("As Of Date", 0)
End Get
Set
MyBase.SetParameterValue("As Of Date", 0, Value)
End Set
End Property
End Class
End Namespace
And the "Handler" does the actual work of populating the return data for a query:-
Option Strict Off
Option Explicit On
Imports CQRSAzure
Imports CQRSAzure.Aggregation
Imports CQRSAzure.EventSourcing
Imports CQRSAzure.QueryDefinition
Imports CQRSAzure.QueryHandler
Imports Herd.Cow
Namespace Herd.Cow.queryHandler
Partial Public Class Get_Herd_Location_Handler
Inherits Object
Implements IGet_Herd_Location_Handler
Public Function HandleQuery() As IEnumerable(Of IGet_Herd_Location_Definition_Return) Implements IGet_Herd_Location_Handler.HandleQuery
Dim queryReturn As IEnumerable(Of IGet_Herd_Location_Definition_Return) = Nothing
Return queryReturn
End Function
End Class
End Namespace
|
|
|
|
|
It turns out that code generation for "Classifiers" is the hardest part.. so far I have:-
Imports CQRSAzure.EventSourcing
Imports CQRSAzure.IdentifierGroup
Imports Football_League.Team
Namespace Football_League.Team.classifier
Partial Public Class Is_Premier_League
Inherits Object
Implements IIs_Premier_League
Sub New()
MyBase.New
End Sub
Private Overloads Function Evaluate(ByVal eventToEvaluate As IRelegated) As EvaluationResult
Implements CQRSAzure.IdentifierGroup.IClassifierEventHandler(Of IRelegated).Evaluate
Return
End Function
Private Overloads Function Evaluate(ByVal eventToEvaluate As IPromoted) As EvaluationResult
Implements CQRSAzure.IdentifierGroup.IClassifierEventHandler(Of IPromoted).Evaluate
End Function
End Class
End Namespace
|
|
|
|
|
...harder than it should be
|
|
|
|
|
So the plan is for a visual tool with which you create your CQRS model and have it CodeGen the required classes for events, projections, queries and commands - and the required documentation (both as code comments and as HTML files for the business users)
Progress is slow but steady - the diagramming is basically there but with some rough edges (using the modelling SDK) and I have started on the code generation using CodeDOM.
I reckon I'm about a month away from a serviceable beta.
(All help gratefully received)
|
|
|
|
|
I can't believe the documentation for the Visual Studio Modelling SDK and the use of it for domain specific languages is so sparse as it is such a powerful feature.
Does everyone who ever works with it:
1) Give up after a certain time or
2) Decide to keep their hard won learning to themselves?
end-of-rant
|
|
|
|
|
* I will spend a lot of time trying to get Azure machine learning experiment set up to do automated stock price anomaly detection. (I will probably not succeed.)
* I will continue to bang away on the "CQRS is brilliant" drum - and maybe actually produce a few code samples.
* I will gain more from CodeProject than I contribute to it, but keep trying to equalise the balance.
|
|
|
|
|
Blood pressure is inversely correlated to oil pressure.
|
|
|
|
|
http://www.merrioncomputing.com/Programming/PrintingNetGuide1.htm
Probably too large to turn into an article here, although there is a significant overlap with the articles I have already uploaded.
All feedback welcomed.
|
|
|
|
|
Hi Duncan,
I just downloaded your ebook "Printing – a .NET developers guide". First of all thank a lot for this very precious document, I could not find anything better and easier explained in a so short document! Congratulation!
I would really appreciate some help and opinion from you as you have experience on print spool monitoring.
I tried to send you an email from your homepage (www.merrioncomputing.com) but some errors on the pages prevented me to send the mail.
Is there a way to contact you? Would really appreciate to make some basics questions about printingQueues and so on. Could you kindly sent your email address to me or contact me? My address is ermeglio71@hotmail.com.
I really hope you can give me some input, would be really apreciated a lot!
thans so much in advance!
ermeglio
|
|
|
|
|
Try the website contact form again. Should be fixed
Otherwise Duncan@ followed by that domain name
|
|
|
|
|
2001 posts in 6 years and 6 months.
Not a high rate by any means...
|
|
|
|
|
|
Getting an experienced developer to do your homework is bad for us, bad for you and bad for society.
It is bad for us because:
* Homework problems are specifically chosen to demonstrate a particular aspect of programming so those of us who have learnt that aspect (and could help) can learn nothing new from them
* It reduces our enthusiasm to help others if we are inundated with homework requests
It is bad for you because:
* Our answers are likely to be more high level than your tutor anticipates so he/she will know it is not your own work
* If we do your homework the only thing you will learn is how not to program. The worlds is already way too full of people who know how not to program.
It is bad for society because:
* If people get by with help they take the place in university, on industrial placement, and everywhere else from people who are willing to learn. So there are less people qualified to do the work, so less people to help others and pretty soon nobody can program...and we can't all be Dilbert pointy hair managers
There is some good news though. We are willing to help you to understand your homework so that you can do it...so if you approach things from that angle we can all get along.
|
|
|
|
|
Wow - the productivity enhancements in .NET 3.5 are really fantastic. I have decided to push as hard as possible for early adoption as I reckon it could nearly double my efficiency.
Specifically:
LINQ
EntityModelCodeGenerator
Intellisense enhancements
|
|
|
|
|
I got the .NET 2.0 release of the PrintQueueWatch component released today - meaning that nearly all my code is now up to date - must be time for .NET 3.0 to come along
'--8<------------------------
Ex Datis:
Duncan Jones
Merrion Computing Ltd
|
|
|
|
|
I get a lot of mail asking for various enhancements to code posted here (and elsewhere) and I do try and incorporate everything even remotely sensible...but this process is pretty slow, because I am very busy at the moment.
What I'm working on is a specialised customer relations management application for EnableIreland and this is currently taking up all of my spare time (and more).
So - if you are waiting for something from me you will need to (a) be patient or (b) help me get the Enable Ireland project done sooner...
'--8<------------------------
Ex Datis:
Duncan Jones
Merrion Computing Ltd
|
|
|
|
|
Just did the first test on the proof of concept application for printout content indexing.
It takes a job when printed and stores the text context of it in an SQL server database for recall (and optional reprinting) at a later date.
Couple of weeks cleaning up and beautifying of the interface and I should have something to show to clients.
Today is a good day.
'--8<------------------------
Ex Datis:
Duncan Jones
Merrion Computing Ltd
|
|
|
|
|