Skip to content

Instantly share code, notes, and snippets.

@CraigCav
Created April 8, 2012 18:51
Show Gist options
  • Save CraigCav/2339152 to your computer and use it in GitHub Desktop.
Save CraigCav/2339152 to your computer and use it in GitHub Desktop.
Patient Waiting times brain dump...
public class ReferralToTreatmentPolicy : Saga<ReferralToTreatmentSagaData>,
IAmStartedByMessages<PatientReferredToConsultantLedService>,
IHandleMessages<TreatmentStarted>,
IHandleMessages<PatientAddedToTransplantList>,
IHandleMessages<PatientDeclinedTreatment>,
IHandleTimeouts<ReferralToTreatmentPeriodBreach>
{
public override void ConfigureHowToFindSaga()
{
ConfigureMapping<TreatmentStarted>(s => s.UBRN, m => m.UniqueBookingReferenceNumber);
ConfigureMapping<PatientAddedToTransplantList>(s => s.UBRN, m => m.UniqueBookingReferenceNumber);
}
public void Handle(PatientReferredToConsultantLedService message)
{
this.Data.UBRN = message.UniqueBookingReferenceNumber;
this.Data.ReferralToTreatmentPeriodStart = message.ReferralRequestReceivedDate;
RequestUtcTimeout<ReferralToTreatmentPeriodBreach>(TimeSpan.FromDays(127),
x => x.BreachDate = message.ReferralRequestReceivedDate.AddDays(127));
}
public override void Timeout(ReferralToTreatmentPeriodBreach state)
{
Bus.Publish<ReferralToTreatmentPeriodBreached>(x =>
x.UBRN = Data.UBRN,
x.ReferralToTreatmentPeriodStart = Data.ReferralToTreatmentPeriodStart,
x.ReferralToTreatmentPeriodBreachDate = x.BreachDate);
}
public void Handle(TreatmentStarted message)
{
if(message.Date < ReferralToTreatmentPeriodStart)
throw new InvalidOperationException("Treatment cannot start before the referral");
Bus.Publish<ReferralToTreatmentPeriodComplete>(x =>
x.UBRN = Data.UBRN
x.ReferralToTreatmentPeriodStart = Data.ReferralToTreatmentPeriodStart,
x.ReferralToTreatmentPeriodEnd = message.Date);
MarkAsComplete();
}
public void Handle(PatientAddedToTransplantList message)
{
if(message.Date < ReferralToTreatmentPeriodStart)
throw new InvalidOperationException("Patient cannot be added to transplant list before the referral");
Bus.Publish<ReferralToTreatmentPeriodComplete>(x =>
x.UBRN = Data.UBRN,
x.ReferralToTreatmentPeriodEnd = message.Date);
MarkAsComplete();
}
public void Handle(PatientDeclinedTreatment message)
{
if(message.Date < ReferralToTreatmentPeriodStart)
throw new InvalidOperationException("Patient cannot decline treatment before the referral");
Bus.Publish<ReferralToTreatmentPeriodComplete>(x =>
x.UBRN = Data.UBRN,
x.ReferralToTreatmentPeriodEnd = message.Date););
MarkAsComplete();
}
}
public interface IAffectReferralToTreatmentTimes
{
Guid UniqueBookingReferenceNumber { get; set; }
}
public interface PatientReferredToConsultantLedService : IAffectReferralToTreatmentTimes
{
Datetime ReferralRequestReceivedDate { get; set; }
}
public interface PatientReferredToOnwardReferralService : PatientReferredToConsultantLedService {}
public interface PatientSelfReferredToConsultantLedService : PatientReferredToConsultantLedService {}
public interface TreatmentStarted : IAffectReferralToTreatmentTimes
{
Datetime Date { get; set; }
}
public interface PatientAddedToTransplantList: IAffectReferralToTreatmentTimes
{
Datetime Date { get; set; }
}
public interface PatientDeclinedTreatment: IAffectReferralToTreatmentTimes
{
Datetime Date { get; set; }
}
public class ReferralToTreatmentSagaData : ISagaEntity
{
public Guid Id { get; set; }
public string Originator { get; set; }
public string OriginalMessageId { get; set; }
[Unique]
public Guid UBRN { get; set; }
public DateTime ReferralToTreatmentPeriodStart { get; set; }
}
@PaulUpson
Copy link

Well, I'm not sure what you want me to tell you about this. I like how simple it appears. Is this based of Udi's saga pattern coz it looks familiar?
I'd maybe like to see some specs/tests surrounding these behaviours but with so few moving parts I can't see it being a problem area.

I can see that this saga has one start condition, one timeout and 3 end conditions. I'm guessing that the MarkAsComplete() terminates the saga, but I'd be interested to see how the the ConfigureMapping actually ensures that the correct saga data is provided to the handlers of subsequent messages.

maybe "next steps" are looking at including clock-pauses?

@CraigCav
Copy link
Author

The implementation in using NServiceBus (ish - it probably doesn't compile) and it's based on my understanding of how Udi describes sagas.

One thing I was exploring with this implementation (and something I'm not sure about yet) is that rather than delegating to an entity to act as the aggregate root, the saga is instead taking this responsibility. This seemed appropriate as the business rules seemed very policy based. That said, I know typically sagas are supposed to act on events and send commands; in my example they act on and send events.

I've toyed with turning the policy into an aggregate root too following a similar approach to Lokad (I'll make this code available on github if you like). This codebase has some specs around it too, but again, something I'm not fully comfortable with is that even in this approach, the aggregate is reacting to events rather than commands. I think this has happened as I want to keep the implementation of waiting times separate from the parts of the system that raise the events (which I would assume come from separate bounded contexts)...but something about this has got my spidey-sense tingling. That said, sagas are meant for collaborations across contexts (from what i'm led to believe)/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment