A fictional company called EvilCorp 😈 wants their employees to be more productive and have decided to implement a system with smart alarm clocks that will wake up their employees at 7am. If the employees have not acknowledged the alarm within 3 snoozes, the alarm will send a message to the headquarters to lay off the employee 😱.
The front-end of the demo is a basic HTML page that uses p5js to draw the information on the page. The back-end is a Dapr application written in ASP.NET that uses actors for the headquarters, the regional office, the employees, and the alarm clocks. Communication between the back-end and the front-end is realized with Ably.
When the demo is run you'll see a realtime display of the alarm clocks in the EvilCorp Dashboard. Alarm clocks are represented by a square. The color of the square indicates the state of the alarm clock:
- Green: Alarm has been acknowledged on time.
- Blue: Alarm has been snoozed.
- Red: Alarm has been snoozed for too long, employee will be laid off.
sequenceDiagram
participant A as API
participant S as Simulation
participant HQ as Headquarters
participant RO as RegionalOffice
participant AC as AlarmClock
participant E as Employee
A->>S: CreateActorProxy
A->>S: InitActorsAsync
S->>HQ: CreateActorProxy
S->>HQ: SetRegionalOfficeIdsAsync
S->>HQ: SetEmployeeIdsAsync
S->>RO: CreateActorProxy
S->>RO: SetRegionalOfficeDataAsync
loop for 1 to employeeCount
S->>AC: CreateActorProxy
S->>AC: SetAlarmClockDataAsync
S->>E: CreateActorProxy
S->>E: SetEmployeeDataAsync
end
S->>RO: SetAlarmClockEmployeeMappingAsync
sequenceDiagram
participant A as API
participant S as Simulation
participant HQ as Headquarters
participant RO as RegionalOffice
participant AC as AlarmClock
participant E as Employee
A->>S: StartTimeAsync
S->>HQ: CreateActorProxy
S->>HQ: GetRegionalOfficeIdsAsync
loop for each regional office
S->>RO: CreateActorProxy
S->>RO: GetRegionalOfficeDataAsync
S->>RO: SetAlarmClockTimeAsync
loop for each alarm clock
RO->>AC: CreateActorProxy
RO->>AC: SetSyncTimeAsync
AC->>AC: RegisterTimerAsync
end
end
loop every second
AC->>AC: IncrementTimeHandler
alt time => alarm time
AC->>AC: AlarmHandler
AC->>RO: CreateActorProxy
AC->>RO: GetEmployeeIdAsync
AC->>E: CreateActorProxy
AC->>E: GetAlarmResponse
alt employee acknowledged
AC->>AC: StopTimerAsync
else employee snoozed
AC->>AC: SnoozeAlarmAsync
end
AC->>AC: GetSnoozeCountAsync
alt snooze count > 3
AC->>RO: CreateActorProxy
AC->>RO: FireEmployeeAsync
RO->>E: CreateActorProxy
RO->>E: SetIsFiredAsync
RO->>HQ: CreateActorProxy
RO->>HQ: FireEmployeeAsync
end
end
end
- Docker Desktop
- Dapr CLI
- Ably account (free tier is sufficient)
-
Copy the Ably API Root key from the Ably portal.
-
Rename the
EvilCorpDemo/EvilCorp.Api/secrets.json.example
file toEvilCorpDemo/EvilCorp.Api/secrets.json
and paste Ably API key in theAblyApiKey
field:{ "AblyApiKey": "YOUR_ABLY_API_KEY_HERE" }
The
secrets.json
file is excluded from source control by the.gitignore
file, so you don't have to worry the API key will be exposed to the public. -
Use the terminal to navigate to the
EvilCorpDemo
folder and build the solution:dotnet build
-
Rename the
EvilCorpDemo/EvilCorp.FrontEnd/secrets.json.example
file toEvilCorpDemo/EvilCorp.FrontEnd/secrets.json
and paste Ably API key in theAblyApiKey
field:{ "AblyApiKey": "YOUR_ABLY_API_KEY_HERE" }
-
Use the terminal to navigate to the
EvilCorpDemo/EvilCorp.FrontEnd
folder and install the dependencies for the front-end:npm install
-
Use the terminal to navigate to the
EvilCorpDemo
folder. -
Use Dapr multi-app run to start both the back-end and front-end with a single command:
dapr run -f .
The
EvilCorpDemo/dapr.yaml
file contains the configuration of the apps that will be started. -
Open the front-end in a browser and click the
Connect
button. This establishes a connection with Ably Realtime. -
Enter the number of employees you want to simulate and click the
Create actors
button. This will create actors for the EvilCorp head quarters, the regional office, the employees and the alarm clocks.Be careful not to create too many employees. The free tier of Ably is rate-limited to handle 70 messages per second. More info in the Ably limits docs.
-
Click the
Start time
button to start the simulation. This will set the time of all the alarm clocks and after each two seconds 10 minutes will pass in the simulation. Once the alarm clocks reach 7:00, the employee actors will randomly acknowledge or snooze the alarm.
The BasicActorSamples
folder in this repo contains a number of samples that demonstrate the basic functionality of Dapr Actors. The samples are written in C# and use the Dapr Actors .NET SDK.
The Actor interactions are performed via the Dapr HTTP API and are listed in the basic-actor-samples.http file.
The HelloWorldActor
is a stateless actor and contains two methods that can be invoked.
The SayHelloWorld
method without parameters:
GET {{ daprHttp }}/v1.0/actors/HelloWorldActor/user1/method/SayHelloWorld
dapr-app-id: basic-actor-demos
The SayHello
method with a parameter:
POST {{ daprHttp }}/v1.0/actors/HelloWorldActor/user2/method/SayHello
dapr-app-id: basic-actor-demos
Content-Type: application/json
"Rene"
The StatefulActor
is a stateful actor and contains two methods, one to set the state and one to get the state.
POST {{ daprHttp }}/v1.0/actors/StatefulActor/user2/method/SetGreeting
dapr-app-id: basic-actor-demos
Content-Type: application/json
"Hello from StatefulActor!"
GET {{ daprHttp }}/v1.0/actors/StatefulActor/user2/method/GetGreeting
dapr-app-id: basic-actor-demos
The state can also be set via the Dapr Actor state endpoint:
POST {{ daprHttp }}/v1.0/actors/StatefulActor/user2/state
dapr-app-id: basic-actor-demos
Content-Type: application/json
[
{
"operation": "upsert",
"request": {
"key": "greeting",
"value": "Hello from StatefulActor (state endpoint)!"
}
}
]
And retrieved via the Dapr Actor state endpoint:
GET {{ daprHttp }}/v1.0/actors/StatefulActor/user2/state/greeting
dapr-app-id: basic-actor-demos
The TimerActor
is an actor that contains a timer. The timer is started when the CreateTimer
method is called and will trigger the execution of the SnoozeHandler
method every 5 seconds.
POST {{ daprHttp }}/v1.0/actors/TimerActor/alarmclock1/method/CreateTimer
dapr-app-id: basic-actor-demos
The timer can be deleted via the Dapr Actor timers endpoint:
DELETE {{ daprHttp }}/v1.0/actors/TimerActor/alarmclock1/timers/snooze
dapr-app-id: basic-actor-demos
The ReminderActor
is an actor that contains a reminder, a stateful version of a timer.
POST {{ daprHttp }}/v1.0/actors/ReminderActor/alarmclock2/reminders/snooze
dapr-app-id: basic-actor-demos
Content-Type: application/json
{
"dueTime" : "0h0m3s0ms",
"period" : "R3/P0Y0M0W0DT0H0M10S"
}
The reminder can be deleted via the Dapr Actor reminders endpoint:
DELETE {{ daprHttp }}/v1.0/actors/ReminderActor/alarmclock2/reminders/snooze
dapr-app-id: basic-actor-demos
The ActorToActor
sample demonstrates how to call an actor from another actor. The CallHelloWorld
method creates a proxy of the HelloWorldActor
and invokes the SayHelloWorld
method on it. This requires the Actor ID of the HelloWorldActor
to be passed as a parameter.
POST {{ daprHttp }}/v1.0/actors/ActorToActor/id1/method/CallHelloWorld
dapr-app-id: basic-actor-demos
Content-Type: application/json
"helloWorldId1"
Any questions or comments about this sample? Join the Dapr discord and post a message the APIS > #actors
channel.
Have you made something with Dapr? Post a message in the #show-and-tell
channel, we love to see your creations!