Skip to content

Instantly share code, notes, and snippets.

@achal7
Created February 16, 2026 21:17
Show Gist options
  • Select an option

  • Save achal7/7df6128e7d8e4ae6dd3ce140303ad447 to your computer and use it in GitHub Desktop.

Select an option

Save achal7/7df6128e7d8e4ae6dd3ce140303ad447 to your computer and use it in GitHub Desktop.
namespace Medhavi.Domain.Calendar
open System
open System.Text.Json.Serialization
open Medhavi.Domain.Ids
[<JsonFSharpConverter>]
type CalendarType =
| ResourceCalendar
| PlantCalendar
| StockingPointCalendar
| NetworkCalendar
[<JsonFSharpConverter>]
type EventId = private | EventId of string
[<JsonFSharpConverter>]
type EventType =
| Holiday
| Maintenance
| Shift
| Campaign
| Custom of string
type RecurrencePattern =
| Daily of int
| Weekly of int * string list // interval, weekdays
| MonthlyDay of int // day of month
| MonthlyWeek of string * string // week of month, day of week
| YearlyDay of int * int // month, day
| YearlyWeek of int * string * string // month, week of month, day of week
type Calendar =
{
Id: CalendarId
CalendarType: CalendarType
BaseDate: DateTimeOffset
WindowDays: int
HistoryDays: int
UpdateInterval: TimeSpan
StartDateAsReal: float option
Events: CalendarEvent list // Events stored in calendar for validation and querying
Created: DateTimeOffset
Modified: DateTimeOffset
}
and CalendarEvent =
{
Id: EventId
CalendarId: CalendarId
Subject: string
EventType: EventType
CapacityFactor: float // 0.0–1.0
IsDefault: bool
Window: Medhavi.Domain.Window
Recurrence: RecurrencePattern option
Created: DateTimeOffset
Modified: DateTimeOffset
}
module EventId =
open Medhavi.Domain
let create (value: string) : Result<EventId, DomainError> =
if String.IsNullOrWhiteSpace value then
Error(DomainError.validation "Event ID is required")
else
Ok(EventId value)
let value (EventId v) = v
type EventSchedule =
{
EventId: EventId
CalendarId: CalendarId
Window: Medhavi.Domain.Window
CapacityFactor: float
}
type CalendarAvailability =
{
CalendarId: CalendarId
Date: DateTimeOffset
CapacityFactor: float
IsAvailable: bool
}
module Medhavi.Domain.Calendar.CalendarWorkflows
open Medhavi.Domain
open Medhavi.Domain.Calendar
open Medhavi.Domain.Ids
open System
open Medhavi.Common.ResultCE
// Commands
type CreateCalendarCmd =
{
Id: CalendarId
CalendarType: CalendarType
IsActive: bool
Created: DateTimeOffset
}
type AddCalendarEventCmd =
{
CalendarId: CalendarId
Event: CalendarEvent
}
type RemoveCalendarEventCmd =
{
CalendarId: CalendarId
EventId: EventId
}
type ClearCalendarCmd =
{
CalendarId: CalendarId
Modified: DateTimeOffset
}
type ActivateCalendarCmd =
{
CalendarId: CalendarId
Modified: DateTimeOffset
}
type DeactivateCalendarCmd =
{
CalendarId: CalendarId
Modified: DateTimeOffset
}
type CalendarCommand =
| CreateCalendar of CreateCalendarCmd
| AddCalendarEvent of AddCalendarEventCmd
| RemoveCalendarEvent of RemoveCalendarEventCmd
| ClearCalendar of ClearCalendarCmd
| ActivateCalendar of ActivateCalendarCmd
| DeactivateCalendar of DeactivateCalendarCmd
// Events
type CalendarCreatedEvt =
{
Id: CalendarId
CalendarType: CalendarType
IsActive: bool
Created: DateTimeOffset
}
type CalendarEventAddedEvt =
{
CalendarId: CalendarId
Event: CalendarEvent
}
type CalendarEventRemovedEvt =
{
CalendarId: CalendarId
EventId: EventId
Modified: DateTimeOffset
}
type CalendarClearedEvt =
{
CalendarId: CalendarId
Modified: DateTimeOffset
}
type CalendarActivatedEvt =
{
CalendarId: CalendarId
Modified: DateTimeOffset
}
type CalendarDeactivatedEvt =
{
CalendarId: CalendarId
Modified: DateTimeOffset
}
type CalendarsEvent =
| CalendarCreated of CalendarCreatedEvt
| CalendarEventAdded of CalendarEventAddedEvt
| CalendarEventRemoved of CalendarEventRemovedEvt
| CalendarCleared of CalendarClearedEvt
| CalendarActivated of CalendarActivatedEvt
| CalendarDeactivated of CalendarDeactivatedEvt
// Signatures
type DecideResourceCalendar = Calendar option -> CalendarCommand -> Result<CalendarEvent list, DomainError>
type EvolveCalendar = Evolve<Calendar, CalendarsEvent>
// Note: Calendar aggregate doesn't have IsActive field, but commands/events do.
// We'll track it separately or extend the Calendar type if needed.
// For now, we'll work with the Calendar structure as defined.
// Validation functions (includes business rules)
let validateCreate (cmd: CreateCalendarCmd) : Result<unit, DomainError> =
result {
//let! _ = required "Calendar Id" cmd.Id
return ()
}
let validateAddEvent (cmd: AddCalendarEventCmd) : Result<unit, DomainError> =
result {
// Validate event has required fields
let! _ =
if String.IsNullOrWhiteSpace(cmd.Event.Subject) then
Error(DomainError.validation "Calendar event subject is required")
else
Ok()
// Validate window
let! _ =
if cmd.Event.Window.Start >= cmd.Event.Window.End then
Error(DomainError.validation "Calendar event start time must be before end time")
else
Ok()
// Validate capacity factor
let! _ =
if
cmd.Event.CapacityFactor < 0.0
|| cmd.Event.CapacityFactor > 1.0
then
Error(DomainError.validation "Calendar event capacity factor must be between 0.0 and 1.0")
else
Ok()
return ()
}
let validateRemoveEvent (cmd: RemoveCalendarEventCmd) : Result<unit, DomainError> = result { return () }
let validateClear (_cmd: ClearCalendarCmd) : Result<unit, DomainError> =
// Clearing is always allowed
Ok()
let validateActivate (_cmd: ActivateCalendarCmd) : Result<unit, DomainError> =
// Activation is always allowed
Ok()
let validateDeactivate (_cmd: DeactivateCalendarCmd) : Result<unit, DomainError> =
// Deactivation is always allowed
Ok()
// State evolution functions (pure state transitions)
// Note: Calendar type doesn't have IsActive, but events do. We'll use default values for missing fields.
let applyCreated (evt: CalendarCreatedEvt) : Calendar =
let now = DateTimeOffset.UtcNow
{
Id = evt.Id
CalendarType = evt.CalendarType
BaseDate = now
WindowDays = 30 // Default window
HistoryDays = 7 // Default history
UpdateInterval = TimeSpan.FromHours(1.0) // Default update interval
StartDateAsReal = None
Events = [] // Initialize with empty events list
Created = evt.Created
Modified = evt.Created
}
// Add event to calendar, maintaining event list
let applyEventAdded (evt: CalendarEventAddedEvt) (state: Calendar) : Calendar =
// Check if event already exists (idempotency)
let eventExists =
state.Events
|> List.exists (fun e -> e.Id = evt.Event.Id)
if eventExists then
state // Idempotent - event already exists
else
{ state with
Events = evt.Event :: state.Events
Modified = DateTimeOffset.UtcNow
}
let applyEventRemoved (evt: CalendarEventRemovedEvt) (state: Calendar) : Calendar =
{ state with
Events =
state.Events
|> List.filter (fun e -> e.Id <> evt.EventId)
Modified = evt.Modified
}
let applyCleared (evt: CalendarClearedEvt) (state: Calendar) : Calendar =
{ state with
Events = [] // Clear all events
Modified = evt.Modified
}
let applyActivated (evt: CalendarActivatedEvt) (state: Calendar) : Calendar = { state with Modified = evt.Modified }
let applyDeactivated (evt: CalendarDeactivatedEvt) (state: Calendar) : Calendar = { state with Modified = evt.Modified }
let evolve (state: Calendar option) (event: CalendarsEvent) : Calendar option =
match event, state with
| CalendarCreated e, None -> Some(applyCreated e)
| CalendarEventAdded e, Some s -> Some(applyEventAdded e s)
| CalendarEventRemoved e, Some s -> Some(applyEventRemoved e s)
| CalendarCleared e, Some s -> Some(applyCleared e s)
| CalendarActivated e, Some s -> Some(applyActivated e s)
| CalendarDeactivated e, Some s -> Some(applyDeactivated e s)
| CalendarCreated _, Some _ -> state // Idempotent - calendar already exists
| _, None -> None // Can't apply updates to non-existent calendar
// let expandRecurrence (evt: CalendarEvent) (horizonStart, horizonEnd) =
// match evt.Recurrence with
// | None -> [ (horizonStart, horizonEnd) ]
// | Daily interval ->
// [
// for i in
// 0 .. int (
// (horizonEnd - horizonStart).TotalDays
// / float interval
// ) ->
// let start = horizonStart.AddDays(float (i * interval))
// (start, start.Add(evt.Duration.Value))
// ]
// | Weekly(interval, weekdays) ->
// // Simplified: generate weekly slots
// []
// | _ -> []
// // Build availability buckets from schedules
// let buildAvailability (schedules: EventSchedule list) (bucketSize: TimeSpan) =
// schedules
// |> List.collect (fun s ->
// let mutable t = s.Start
// let buckets = ResizeArray()
// while t < s.End do
// buckets.Add(
// {
// CalendarId = s.CalendarId
// Date = t
// CapacityFactor = s.CapacityFactor
// IsAvailable = s.CapacityFactor > 0.0
// }
// )
// t <- t.Add(bucketSize)
// buckets |> List.ofSeq)
// //
namespace Medhavi.Domain.Capacity.Reservation
open System
open Medhavi.Domain.Ids
open Medhavi.Domain
open System.Text.Json.Serialization
[<JsonFSharpConverter>]
type CapacityReservationStatus =
| Tentative
| Confirmed
| Released
| Expired
| Reduced
type CapacityReservation =
{
Id: CapacityReservationId
IdempotencyKey: string option
ResourceId: PhysicalResourceId
ReservedCapacity: TimeSpan
Status: CapacityReservationStatus
Window: Window
Ttl: TimeSpan option
OperationId: OperationId option
CreatedDate: DateTimeOffset
ModifiedDate: DateTimeOffset
}
// Commands
type CreateCapacityReservationCmd =
{
Id: string
IdempotencyKey: string option
ResourceId: PhysicalResourceId
ReservedCapacity: TimeSpan
Window: Window
Ttl: TimeSpan option
OperationId: OperationId
CreatedDate: DateTimeOffset
}
type ConfirmCapacityReservationCmd =
{
Id: CapacityReservationId
ConfirmedDate: DateTimeOffset
}
type ReleaseCapacityReservationCmd =
{
Id: CapacityReservationId
ReleasedDate: DateTimeOffset
}
type ReduceCapacityReservationCmd =
{
Id: CapacityReservationId
NewCapacity: TimeSpan
NewWindowEnd: DateTimeOffset option
ReducedDate: DateTimeOffset
}
type ExpireCapacityReservationCmd =
{
Id: CapacityReservationId
ExpiredDate: DateTimeOffset
}
type CapacityReservationCommand =
| CreateCapacityReservation of CreateCapacityReservationCmd
| ConfirmCapacityReservation of ConfirmCapacityReservationCmd
| ReleaseCapacityReservation of ReleaseCapacityReservationCmd
| ReduceCapacityReservation of ReduceCapacityReservationCmd
| ExpireCapacityReservation of ExpireCapacityReservationCmd
// Events
type CapacityReservationCreatedEvt =
{
Id: CapacityReservationId
IdempotencyKey: string option
ResourceId: PhysicalResourceId
ReservedCapacity: TimeSpan
Window: Window
Ttl: TimeSpan option
OperationId: OperationId
CreatedDate: DateTimeOffset
}
type CapacityReservationConfirmedEvt =
{
Id: CapacityReservationId
ConfirmedDate: DateTimeOffset
}
type CapacityReservationReleasedEvt =
{
Id: CapacityReservationId
ReleasedDate: DateTimeOffset
}
type CapacityReservationReducedEvt =
{
Id: CapacityReservationId
NewCapacity: TimeSpan
NewWindowEnd: DateTimeOffset option
}
type CapacityReservationExpiredEvt =
{
Id: CapacityReservationId
ExpiredDate: DateTimeOffset
}
type CapacityReservationEvent =
| CapacityReservationCreated of CapacityReservationCreatedEvt
| CapacityReservationConfirmed of CapacityReservationConfirmedEvt
| CapacityReservationReleased of CapacityReservationReleasedEvt
| CapacityReservationReduced of CapacityReservationReducedEvt
| CapacityReservationExpired of CapacityReservationExpiredEvt
// Signatures
type DecideCapacityReservation =
CapacityReservation option -> CapacityReservationCommand -> Result<CapacityReservationEvent list, DomainError>
type EvolveCapacityReservation = Evolve<CapacityReservation, CapacityReservationEvent>
namespace Medhavi.Domain.Resources
open System
open Medhavi.Domain.Calendar
open Medhavi.Domain.Ids
open Medhavi.Domain
open ResourceGroup
type CombinedResource =
{
Id: CombinedResourceId
Name: string
StandardResources: StandardResourceId list
IsActive: bool
Created: DateTimeOffset
Modified: DateTimeOffset
}
type CombinedResourcePeriod =
{
Id: CombinedResourcePeriodId
CombinedResourceId: CombinedResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
Created: DateTimeOffset
Modified: DateTimeOffset
}
// Commands
type DefineCombinedResourceCmd =
{
Id: string
Name: string
StandardResources: StandardResourceId list
IsActive: bool
}
type RenameCombinedResourceCmd =
{
Id: CombinedResourceId
NewName: string
}
type DeactivateCombinedResourceCmd =
{
Id: CombinedResourceId
DeactivatedAt: DateTimeOffset
}
type DefineCombinedResourcePeriodCmd =
{
Id: string
CombinedResourceId: CombinedResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
}
type CombinedResourceCommand =
| DefineCombinedResource of DefineCombinedResourceCmd
| RenameCombinedResource of RenameCombinedResourceCmd
| DeactivateCombinedResource of DeactivateCombinedResourceCmd
| DefineCombinedResourcePeriod of DefineCombinedResourcePeriodCmd
// Events
type CombinedResourceDefinedEvt =
{
Id: CombinedResourceId
Name: string
StandardResources: StandardResourceId list
IsActive: bool
Created: DateTimeOffset
}
type CombinedResourceRenamedEvt =
{
Id: CombinedResourceId
NewName: string
Modified: DateTimeOffset
}
type CombinedResourceDeactivatedEvt =
{
Id: CombinedResourceId
DeactivatedAt: DateTimeOffset
}
type CombinedResourcePeriodDefinedEvt =
{
Id: CombinedResourcePeriodId
CombinedResourceId: CombinedResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
Created: DateTimeOffset
}
type CombinedResourceEvent =
| CombinedResourceDefined of CombinedResourceDefinedEvt
| CombinedResourceRenamed of CombinedResourceRenamedEvt
| CombinedResourceDeactivated of CombinedResourceDeactivatedEvt
| CombinedResourcePeriodDefined of CombinedResourcePeriodDefinedEvt
// Signatures
type DecideCombinedResource =
CombinedResource option -> CombinedResourceCommand -> Result<CombinedResourceEvent list, DomainError>
type EvolveCombinedResource = CombinedResource option -> CombinedResourceEvent -> CombinedResource
namespace Medhavi.Domain.Capacity.Operation
open System
open Medhavi.Domain.Ids
open Medhavi.Domain
type OperationState =
| Created
| Planned
| Scheduled
| InProgress
| Completed
| Cancelled
type Operation =
{
Id: OperationId
SequenceNumber: int
RoutingStepId: RoutingStepId
State: OperationState
AddedLeadTime: TimeSpan
IsFixed: bool
CampaignTypeAssignment: CampaignTypeAssignmentId option
// Capacity-related fields (mandatory when Scheduled/InProgress)
Window: Window option // Mandatory when State = Scheduled or InProgress
ResourceId: PhysicalResourceId option // Mandatory when State = Scheduled or InProgress
Duration: TimeSpan option // Mandatory when State = Scheduled or InProgress
CreatedDate: DateTimeOffset
ModifiedDate: DateTimeOffset
}
// Commands
type ScheduleOperationCmd =
{
Id: OperationId
SequenceNumber: int
Window: Window // Mandatory: when operation is scheduled, it must have a time window
RoutingStepId: RoutingStepId
ResourceId: PhysicalResourceId // Mandatory: which resource will perform this operation
Duration: TimeSpan // Mandatory: how long the operation will take
IsFixed: bool
}
type StartOperationCmd =
{
Id: OperationId
StartedDate: DateTimeOffset
}
type CompleteOperationCmd =
{
Id: OperationId
CompletedDate: DateTimeOffset
}
type CancelOperationCmd =
{
Id: OperationId
CancelledDate: DateTimeOffset
}
type OperationCommand =
| ScheduleOperation of ScheduleOperationCmd
| StartOperation of StartOperationCmd
| CompleteOperation of CompleteOperationCmd
| CancelOperation of CancelOperationCmd
// Events
type OperationScheduledEvt =
{
Id: OperationId
Window: Window // Time window when operation is scheduled
ResourceId: PhysicalResourceId // Resource that will perform the operation
Duration: TimeSpan // Duration of the operation
}
type OperationStartedEvt =
{
Id: OperationId
StartedDate: DateTimeOffset
}
type OperationCompletedEvt =
{
Id: OperationId
CompletedDate: DateTimeOffset
}
type OperationCancelledEvt =
{
Id: OperationId
CancelledDate: DateTimeOffset
}
type OperationEvent =
| OperationScheduled of OperationScheduledEvt
| OperationStarted of OperationStartedEvt
| OperationCompleted of OperationCompletedEvt
| OperationCancelled of OperationCancelledEvt
// Signatures
type DecideOperation = Medhavi.Domain.Decide<Operation, OperationCommand, OperationEvent, DomainError>
type EvolveOperation = Medhavi.Domain.Evolve<Operation, OperationEvent>
module Medhavi.Domain.Resources.PhysicalResource
open System
open Medhavi.Domain.Calendar
open Medhavi.Domain.Ids
open Medhavi.Domain.Validation
open Medhavi.Domain
open Medhavi.Common.ResultCE
open ResourceGroup
open StandardResource
type PhysicalResource =
{
Id: PhysicalResourceId
StandardResourceId: StandardResourceId
Name: string
SerialNumber: string option
Location: string option
IsActive: bool
Created: DateTimeOffset
Modified: DateTimeOffset
}
type PhysicalResourcePeriod =
{
Id: PhysicalResourcePeriodId
PhysicalResourceId: PhysicalResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
Created: DateTimeOffset
Modified: DateTimeOffset
}
// Commands
type DefinePhysicalResourceCmd =
{
Id: string
StandardResourceId: StandardResourceId
Name: string
SerialNumber: string option
Location: string option
IsActive: bool
}
type RenamePhysicalResourceCmd =
{
Id: PhysicalResourceId
NewName: string
}
type DeactivatePhysicalResourceCmd =
{
Id: PhysicalResourceId
DeactivatedAt: DateTimeOffset
}
type DefinePhysicalResourcePeriodCmd =
{
Id: string
PhysicalResourceId: PhysicalResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
}
type PhysicalResourceCommand =
| DefinePhysicalResource of DefinePhysicalResourceCmd
| RenamePhysicalResource of RenamePhysicalResourceCmd
| DeactivatePhysicalResource of DeactivatePhysicalResourceCmd
| DefinePhysicalResourcePeriod of DefinePhysicalResourcePeriodCmd
// Events
type PhysicalResourceDefinedEvt =
{
Id: PhysicalResourceId
StandardResourceId: StandardResourceId
Name: string
SerialNumber: string option
Location: string option
IsActive: bool
Created: DateTimeOffset
}
type PhysicalResourceRenamedEvt =
{
Id: PhysicalResourceId
NewName: string
Modified: DateTimeOffset
}
type PhysicalResourceDeactivatedEvt =
{
Id: PhysicalResourceId
DeactivatedAt: DateTimeOffset
}
type PhysicalResourcePeriodDefinedEvt =
{
Id: PhysicalResourcePeriodId
PhysicalResourceId: PhysicalResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
Created: DateTimeOffset
}
type PhysicalResourceEvent =
| PhysicalResourceDefined of PhysicalResourceDefinedEvt
| PhysicalResourceRenamed of PhysicalResourceRenamedEvt
| PhysicalResourceDeactivated of PhysicalResourceDeactivatedEvt
| PhysicalResourcePeriodDefined of PhysicalResourcePeriodDefinedEvt
// Signatures
type DecidePhysicalResource =
PhysicalResource option -> PhysicalResourceCommand -> Result<PhysicalResourceEvent list, DomainError>
type EvolvePhysicalResource = Medhavi.Domain.Evolve<PhysicalResource, PhysicalResourceEvent>
// Validation functions (includes business rules)
let validateDefine (cmd: DefinePhysicalResourceCmd) : Result<unit, DomainError> =
result {
let! _ = required "PhysicalResource Id" cmd.Id
let! _ = required "PhysicalResource Name" cmd.Name
return ()
}
let validateRename (cmd: RenamePhysicalResourceCmd) : Result<unit, DomainError> =
result {
let! _ =
if String.IsNullOrWhiteSpace cmd.NewName then
Error(DomainError.validation "Physical resource name cannot be empty")
else
Ok()
return ()
}
let validateDeactivate (_cmd: DeactivatePhysicalResourceCmd) : Result<unit, DomainError> =
// Deactivation is always allowed
Ok()
// State evolution functions (pure state transitions)
let applyDefined (evt: PhysicalResourceDefinedEvt) : PhysicalResource =
{
Id = evt.Id
StandardResourceId = evt.StandardResourceId
Name = evt.Name
SerialNumber = evt.SerialNumber
Location = evt.Location
IsActive = evt.IsActive
Created = evt.Created
Modified = evt.Created
}
let applyRenamed (evt: PhysicalResourceRenamedEvt) (state: PhysicalResource) : PhysicalResource =
{ state with
Name = evt.NewName
Modified = evt.Modified
}
let applyDeactivated (evt: PhysicalResourceDeactivatedEvt) (state: PhysicalResource) : PhysicalResource =
{ state with
IsActive = false
Modified = evt.DeactivatedAt
}
let evolve (state: PhysicalResource option) (event: PhysicalResourceEvent) : PhysicalResource option =
match event, state with
| PhysicalResourceDefined e, None -> Some(applyDefined e)
| PhysicalResourceRenamed e, Some s -> Some(applyRenamed e s)
| PhysicalResourceDeactivated e, Some s -> Some(applyDeactivated e s)
| PhysicalResourceDefined _, Some _ -> state // Idempotent - physical resource already exists
| _, None -> None // Can't apply updates to non-existent physical resource
| _ -> state // Other events not handled
module Medhavi.Domain.Resources.ResourceGroup
open System
open Medhavi.Domain.Ids
open Medhavi.Domain.Calendar
open Medhavi.Domain.Validation
open Medhavi.Domain
open Medhavi.Common.ResultCE
type CapacityBreakdown =
{
TotalCapacity: TimeSpan
AvailableCapacity: TimeSpan
UnavailableCapacity: TimeSpan
ReservedCapacity: TimeSpan
OccupiedCapacity: TimeSpan
OverloadCapacity: TimeSpan
FreeCapacity: TimeSpan
}
type ResourceGroup =
{
Id: ResourceGroupId
PlantId: PlantId option
Name: string
Description: string option
IsActive: bool
CalendarId: CalendarId option
Created: DateTimeOffset
Modified: DateTimeOffset
}
type ResourceGroupCapacity =
{
Id: ResourceGroupCapacityId
ResourceGroupId: ResourceGroupId
Window: Window
Capacity: CapacityBreakdown
Created: DateTimeOffset
Modified: DateTimeOffset
}
// Commands
type DefineResourceGroupCmd =
{
Id: string
PlantId: PlantId option
Name: string
Description: string option
IsActive: bool
}
type RenameResourceGroupCmd =
{ Id: ResourceGroupId; NewName: string }
type DeactivateResourceGroupCmd =
{
Id: ResourceGroupId
DeactivatedAt: DateTimeOffset
}
type DefineResourceGroupPeriodCmd =
{
Id: string
ResourceGroupId: ResourceGroupId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
}
type ResourceGroupCommand =
| DefineResourceGroup of DefineResourceGroupCmd
| RenameResourceGroup of RenameResourceGroupCmd
| DeactivateResourceGroup of DeactivateResourceGroupCmd
| DefineResourceGroupPeriod of DefineResourceGroupPeriodCmd
// Events
type ResourceGroupDefinedEvt =
{
Id: ResourceGroupId
PlantId: PlantId option
Name: string
Description: string option
IsActive: bool
Created: DateTimeOffset
}
type ResourceGroupRenamedEvt =
{
Id: ResourceGroupId
NewName: string
Modified: DateTimeOffset
}
type ResourceGroupDeactivatedEvt =
{
Id: ResourceGroupId
DeactivatedAt: DateTimeOffset
}
type ResourceGroupEvent =
| ResourceGroupDefined of ResourceGroupDefinedEvt
| ResourceGroupRenamed of ResourceGroupRenamedEvt
| ResourceGroupDeactivated of ResourceGroupDeactivatedEvt
// Signatures
type DecideResourceGroup = ResourceGroup option -> ResourceGroupCommand -> Result<ResourceGroupEvent list, DomainError>
type EvolveResourceGroup = Medhavi.Domain.Evolve<ResourceGroup, ResourceGroupEvent>
// Validation functions (includes business rules)
let validateDefine (cmd: DefineResourceGroupCmd) : Result<unit, DomainError> =
result {
let! _ = required "ResourceGroup Id" cmd.Id
let! _ = required "ResourceGroup Name" cmd.Name
return ()
}
let validateRename (cmd: RenameResourceGroupCmd) : Result<unit, DomainError> =
result {
let! _ =
if String.IsNullOrWhiteSpace cmd.NewName then
Error(DomainError.validation "Resource group name cannot be empty")
else
Ok()
return ()
}
let validateDeactivate (_cmd: DeactivateResourceGroupCmd) : Result<unit, DomainError> =
// Deactivation is always allowed
Ok()
// State evolution functions (pure state transitions)
let applyDefined (evt: ResourceGroupDefinedEvt) : ResourceGroup =
{
Id = evt.Id
PlantId = evt.PlantId
Name = evt.Name
Description = evt.Description
IsActive = evt.IsActive
CalendarId = None
Created = evt.Created
Modified = evt.Created
}
let applyRenamed (evt: ResourceGroupRenamedEvt) (state: ResourceGroup) : ResourceGroup =
{ state with
Name = evt.NewName
Modified = evt.Modified
}
let applyDeactivated (evt: ResourceGroupDeactivatedEvt) (state: ResourceGroup) : ResourceGroup =
{ state with
IsActive = false
Modified = evt.DeactivatedAt
}
let evolve (state: ResourceGroup option) (event: ResourceGroupEvent) : ResourceGroup option =
match event, state with
| ResourceGroupDefined e, None -> Some(applyDefined e)
| ResourceGroupRenamed e, Some s -> Some(applyRenamed e s)
| ResourceGroupDeactivated e, Some s -> Some(applyDeactivated e s)
| ResourceGroupDefined _, Some _ -> state // Idempotent - resource group already exists
| _, None -> None // Can't apply updates to non-existent resource group
module Medhavi.Domain.Resources.StandardResource
open System
open Medhavi.Domain.Calendar
open Medhavi.Domain.Ids
open Medhavi.Domain.Validation
open Medhavi.Domain
open Medhavi.Common.ResultCE
open ResourceGroup
type StandardResource =
{
Id: StandardResourceId
ResourceGroupId: ResourceGroupId
Name: string
Description: string option
PhysicalResources: PhysicalResourceId list
CampaignTypes: CampaignTypeId list
IsActive: bool
Created: DateTimeOffset
Modified: DateTimeOffset
}
type StandardResourcePeriod =
{
Id: StandardResourcePeriodId
StandardResourceId: StandardResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
EfficiencyFactor: float
Created: DateTimeOffset
Modified: DateTimeOffset
}
// Commands
type DefineStandardResourceCmd =
{
Id: string
ResourceGroupId: ResourceGroupId
Name: string
Description: string option
IsActive: bool
}
type RenameStandardResourceCmd =
{
Id: StandardResourceId
NewName: string
}
type DeactivateStandardResourceCmd =
{
Id: StandardResourceId
DeactivatedAt: DateTimeOffset
}
type DefineStandardResourcePeriodCmd =
{
Id: string
StandardResourceId: StandardResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
EfficiencyFactor: float
}
type StandardResourceCommand =
| DefineStandardResource of DefineStandardResourceCmd
| RenameStandardResource of RenameStandardResourceCmd
| DeactivateStandardResource of DeactivateStandardResourceCmd
| DefineStandardResourcePeriod of DefineStandardResourcePeriodCmd
// Events
type StandardResourceDefinedEvt =
{
Id: StandardResourceId
ResourceGroupId: ResourceGroupId
Name: string
Description: string option
IsActive: bool
Created: DateTimeOffset
}
type StandardResourceRenamedEvt =
{
Id: StandardResourceId
NewName: string
Modified: DateTimeOffset
}
type StandardResourceDeactivatedEvt =
{
Id: StandardResourceId
DeactivatedAt: DateTimeOffset
}
type StandardResourcePeriodDefinedEvt =
{
Id: StandardResourcePeriodId
StandardResourceId: StandardResourceId
StartTime: DateTimeOffset
EndTime: DateTimeOffset
Capacity: CapacityBreakdown
EfficiencyFactor: float
Created: DateTimeOffset
}
type StandardResourceEvent =
| StandardResourceDefined of StandardResourceDefinedEvt
| StandardResourceRenamed of StandardResourceRenamedEvt
| StandardResourceDeactivated of StandardResourceDeactivatedEvt
| StandardResourcePeriodDefined of StandardResourcePeriodDefinedEvt
// Signatures
type DecideStandardResource =
StandardResource option -> StandardResourceCommand -> Result<StandardResourceEvent list, DomainError>
type EvolveStandardResource = Medhavi.Domain.Evolve<StandardResource, StandardResourceEvent>
// Validation functions (includes business rules)
let validateDefine (cmd: DefineStandardResourceCmd) : Result<unit, DomainError> =
result {
let! _ = required "StandardResource Id" cmd.Id
let! _ = required "StandardResource Name" cmd.Name
return ()
}
let validateRename (cmd: RenameStandardResourceCmd) : Result<unit, DomainError> =
result {
let! _ =
if String.IsNullOrWhiteSpace cmd.NewName then
Error(DomainError.validation "Standard resource name cannot be empty")
else
Ok()
return ()
}
let validateDeactivate (_cmd: DeactivateStandardResourceCmd) : Result<unit, DomainError> =
// Deactivation is always allowed
Ok()
// State evolution functions (pure state transitions)
let applyDefined (evt: StandardResourceDefinedEvt) : StandardResource =
{
Id = evt.Id
ResourceGroupId = evt.ResourceGroupId
Name = evt.Name
Description = evt.Description
PhysicalResources = []
CampaignTypes = []
IsActive = evt.IsActive
Created = evt.Created
Modified = evt.Created
}
let applyRenamed (evt: StandardResourceRenamedEvt) (state: StandardResource) : StandardResource =
{ state with
Name = evt.NewName
Modified = evt.Modified
}
let applyDeactivated (evt: StandardResourceDeactivatedEvt) (state: StandardResource) : StandardResource =
{ state with
IsActive = false
Modified = evt.DeactivatedAt
}
let evolve (state: StandardResource option) (event: StandardResourceEvent) : StandardResource option =
match event, state with
| StandardResourceDefined e, None -> Some(applyDefined e)
| StandardResourceRenamed e, Some s -> Some(applyRenamed e s)
| StandardResourceDeactivated e, Some s -> Some(applyDeactivated e s)
| StandardResourceDefined _, Some _ -> state // Idempotent - standard resource already exists
| _, None -> None // Can't apply updates to non-existent standard resource
| _ -> state // Other events not handled
module Medhavi.Domain.Capacity.Telemetry
open System
open Medhavi.Domain.TelemetryContracts
open Medhavi.Domain
open Medhavi.Domain.Ids
let utilization (telemetry: TelemetryProvider) (resource: string) (used: TimeSpan) (available: TimeSpan) =
telemetry.RecordKpi(
KpiEvent.CapacityUtilization(resource, int used.TotalMilliseconds, int available.TotalMilliseconds)
)
let allocationLatency (telemetry: TelemetryProvider) (stepId: string) (latency: TimeSpan) =
telemetry.RecordKpi(KpiEvent.CapacityAllocationLatency(stepId, int latency.TotalMilliseconds))
let bottleneckDetected (telemetry: TelemetryProvider) (resource: string) (stepId: string) =
telemetry.RecordKpi(KpiEvent.CapacityBottleneckDetected(resource, stepId))
let churn (telemetry: TelemetryProvider) (cycleId: string) (allocationsChanged: int) =
telemetry.RecordKpi(KpiEvent.CapacityChurn(cycleId, allocationsChanged))
let lockAdherence (telemetry: TelemetryProvider) (resource: string) (reserved: TimeSpan) (used: TimeSpan) =
telemetry.RecordKpi(
KpiEvent.CapacityLockAdherence(resource, int reserved.TotalMilliseconds, int used.TotalMilliseconds)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment