diff --git a/README.md b/README.md index e915148..614013a 100644 --- a/README.md +++ b/README.md @@ -33,30 +33,62 @@ Ideally on my birthday, May 22nd! ## 🏃 Running -Be sure to launch EventStoreDB through Docker. Through a terminal / command prompt: +### 0️⃣ Project: StudentEnrollment00 + +Like the other programs that are .NET console application, the database is a glorified key-value store that's in-memory. + +To run it, enter the project directory through your shell's associated Change Directory (`cd`) command from the solution root, such as: ```bash -docker-compose up +cd .\StudentEnrollment01 ``` -You can build the entire solution, including all versions of the projects, by running the following at the root of `/StudentEnrollmentConsoleApp/` with: +and then build the project ```bash dotnet build ``` +and run it + +```bash +dotnet run +``` + +Alternatively, you can do all this from the solution's root by targeting the project with `--project` and `run` it immediately, such as: + +```bash +dotnet run --project .\StudentEnrollment00\StudentEnrollment00.csproj +``` + +### 1️⃣ Project: StudentEnrollment01 + +While still a .NET console app, we're now going to use the Event Store database. So be sure to launch EventStoreDB through Docker. With a terminal / command prompt execute: + +```bash +docker-compose up +``` + +Like before, you can change directory into the project or run it from the root. + Run the console application of your choice, change direction to that particular project directory and then execute: ```bash +cd .\StudentEnrollment01 +dotnet build dotnet run ``` -Alternatively, you can stay in the root directory `/StudentEnrollmentConsoleApp/` and include the path to the intended project you want to have run. This same operation can be done with the previous `dotnet build` command, too. +Or alternatively: ```bash -dotnet run .\StudentEnrollment01\StudentEnrollment01.csproj +dotnet run --project .\StudentEnrollment01\StudentEnrollment01.csproj ``` +### 2️⃣ TBD + +Instructions will go here, but will basically be the same as above! + ## 🔗 Resources Erik Shafer (me): diff --git a/StudentEnrollment00/Events/Event.cs b/StudentEnrollment00/Events/Event.cs new file mode 100644 index 0000000..f98a116 --- /dev/null +++ b/StudentEnrollment00/Events/Event.cs @@ -0,0 +1,8 @@ +namespace StudentEnrollment00.Events; + +public abstract class Event +{ + public abstract Guid StreamId { get; } + + public DateTime CreatedAtUtc { get; set; } +} \ No newline at end of file diff --git a/StudentEnrollment00/Events/StudentCreated.cs b/StudentEnrollment00/Events/StudentCreated.cs new file mode 100644 index 0000000..5dd0e21 --- /dev/null +++ b/StudentEnrollment00/Events/StudentCreated.cs @@ -0,0 +1,11 @@ +namespace StudentEnrollment00.Events; + +public class StudentCreated : Event +{ + public required Guid StudentId { get; init; } + public required string FullName { get; init; } + public required string Email { get; init; } + public required DateTime DateOfBirth { get; init; } + + public override Guid StreamId => StudentId; +} \ No newline at end of file diff --git a/StudentEnrollment00/Events/StudentEnrolled.cs b/StudentEnrollment00/Events/StudentEnrolled.cs new file mode 100644 index 0000000..f75c385 --- /dev/null +++ b/StudentEnrollment00/Events/StudentEnrolled.cs @@ -0,0 +1,9 @@ +namespace StudentEnrollment00.Events; + +public class StudentEnrolled : Event +{ + public required Guid StudentId { get; init; } + public required string CourseName { get; init; } + + public override Guid StreamId => StudentId; +} \ No newline at end of file diff --git a/StudentEnrollment00/Events/StudentUnEnrolled.cs b/StudentEnrollment00/Events/StudentUnEnrolled.cs new file mode 100644 index 0000000..be810b7 --- /dev/null +++ b/StudentEnrollment00/Events/StudentUnEnrolled.cs @@ -0,0 +1,9 @@ +namespace StudentEnrollment00.Events; + +public class StudentUnEnrolled : Event +{ + public required Guid StudentId { get; init; } + public required string CourseName { get; init; } + + public override Guid StreamId => StudentId; +} \ No newline at end of file diff --git a/StudentEnrollment00/Events/StudentUpdated.cs b/StudentEnrollment00/Events/StudentUpdated.cs new file mode 100644 index 0000000..9c293f9 --- /dev/null +++ b/StudentEnrollment00/Events/StudentUpdated.cs @@ -0,0 +1,10 @@ +namespace StudentEnrollment00.Events; + +public class StudentUpdated : Event +{ + public required Guid StudentId { get; init; } + public required string FullName { get; init; } + public required string Email { get; init; } + + public override Guid StreamId => StudentId; +} \ No newline at end of file diff --git a/StudentEnrollment00/InMemoryDatabase.cs b/StudentEnrollment00/InMemoryDatabase.cs new file mode 100644 index 0000000..f0643ee --- /dev/null +++ b/StudentEnrollment00/InMemoryDatabase.cs @@ -0,0 +1,34 @@ +using StudentEnrollment00.Events; + +namespace StudentEnrollment00; + +public sealed class InMemoryDatabase +{ + private readonly Dictionary> _studentEvents = new(); + + public void Append(Event @event) + { + var stream = _studentEvents!.GetValueOrDefault(@event.StreamId, null); + if (stream is null) + { + _studentEvents[@event.StreamId] = new SortedList(); + } + + @event.CreatedAtUtc = DateTime.UtcNow; + _studentEvents[@event.StreamId].Add(@event.CreatedAtUtc, @event); + } + + public Student? GetStudent(Guid id) + { + if (_studentEvents.ContainsKey(id) is false) + return null; + + var student = new Student(); + var studentEvents = _studentEvents[id]; + + foreach (var @event in studentEvents) + student.Apply(@event.Value); + + return student; + } +} \ No newline at end of file diff --git a/StudentEnrollment00/Program.cs b/StudentEnrollment00/Program.cs new file mode 100644 index 0000000..8b71044 --- /dev/null +++ b/StudentEnrollment00/Program.cs @@ -0,0 +1,24 @@ +using StudentEnrollment00; +using StudentEnrollment00.Events; + +var id = Guid.Parse("a662d446-4920-415e-8c2a-0dd4a6c58908"); +var now = DateTime.Now; + +var studentCreated = new StudentCreated +{ + CreatedAtUtc = now.ToUniversalTime(), + StudentId = id, + FullName = "Erik Shafer", + Email = "erik.shafer@eventstore.com", + DateOfBirth = new DateTime(1987, 1, 1) +}; + +var inMemoryDb = new InMemoryDatabase(); +inMemoryDb.Append(studentCreated); + +var student = inMemoryDb.GetStudent(id); + +Console.WriteLine( + "StudentId: {0} | FullName: {1} | Email: {2} | DOB: {3} | CreatedAtUtc: {4}", + student!.Id, student.FullName, student.Email, student.DateOfBirth, student.CreatedAtUtc); +Console.WriteLine(); \ No newline at end of file diff --git a/StudentEnrollment00/Student.cs b/StudentEnrollment00/Student.cs new file mode 100644 index 0000000..75c7596 --- /dev/null +++ b/StudentEnrollment00/Student.cs @@ -0,0 +1,58 @@ +using StudentEnrollment00.Events; + +namespace StudentEnrollment00; + +public class Student +{ + public Guid Id { get; set; } = default!; + public string FullName { get; set; } = default!; + public string Email { get; set; } = default!; + public DateTime DateOfBirth { get; set; } + public DateTime CreatedAtUtc { get; set; } + public List EnrolledCourses { get; set; } = []; + + public void Apply(Event @event) + { + switch (@event) + { + case StudentCreated created: + Apply(created); + break; + case StudentUpdated updated: + Apply(updated); + break; + case StudentEnrolled enrolled: + Apply(enrolled); + break; + case StudentUnEnrolled unEnrolled: + Apply(unEnrolled); + break; + } + } + + private void Apply(StudentCreated @event) + { + Id = @event.StudentId; + FullName = @event.FullName; + Email = @event.Email; + } + + private void Apply(StudentUpdated @event) + { + Id = @event.StudentId; // Updating the identity, huh? Interesting... 👀 + FullName = @event.FullName; + Email = @event.Email; + } + + private void Apply(StudentEnrolled @event) + { + if (EnrolledCourses.Contains(@event.CourseName) is false) + EnrolledCourses.Add(@event.CourseName); + } + + private void Apply(StudentUnEnrolled @event) + { + if (EnrolledCourses.Contains(@event.CourseName)) + EnrolledCourses.Add(@event.CourseName); + } +} \ No newline at end of file diff --git a/StudentEnrollment00/StudentEnrollment00.csproj b/StudentEnrollment00/StudentEnrollment00.csproj new file mode 100644 index 0000000..e407cdc --- /dev/null +++ b/StudentEnrollment00/StudentEnrollment00.csproj @@ -0,0 +1,11 @@ + + + + Exe + net8.0 + enable + enable + Linux + + + diff --git a/StudentEnrollment01/Events/Event.cs b/StudentEnrollment01/Events/Event.cs index b872c40..5c343f5 100644 --- a/StudentEnrollment01/Events/Event.cs +++ b/StudentEnrollment01/Events/Event.cs @@ -3,4 +3,5 @@ namespace StudentEnrollment01.Events; public abstract record Event { public string Id { get; init; } = default!; + public DateTime CreatedAtUtc { get; set; } } \ No newline at end of file diff --git a/StudentEnrollmentConsoleApp.sln b/StudentEnrollmentConsoleApp.sln index 67b49a4..09543ff 100644 --- a/StudentEnrollmentConsoleApp.sln +++ b/StudentEnrollmentConsoleApp.sln @@ -10,6 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StudentEnrollment00", "StudentEnrollment00\StudentEnrollment00.csproj", "{B8D543AF-F986-4E50-AD1D-492B38A58ECF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,5 +22,9 @@ Global {446FF6B7-B58B-4CFB-8E98-C5B3AED9BA4D}.Debug|Any CPU.Build.0 = Debug|Any CPU {446FF6B7-B58B-4CFB-8E98-C5B3AED9BA4D}.Release|Any CPU.ActiveCfg = Release|Any CPU {446FF6B7-B58B-4CFB-8E98-C5B3AED9BA4D}.Release|Any CPU.Build.0 = Release|Any CPU + {B8D543AF-F986-4E50-AD1D-492B38A58ECF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8D543AF-F986-4E50-AD1D-492B38A58ECF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8D543AF-F986-4E50-AD1D-492B38A58ECF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8D543AF-F986-4E50-AD1D-492B38A58ECF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal