diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cd967fc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/README.md b/README.md index 8b1bae1..4649745 100644 --- a/README.md +++ b/README.md @@ -47,35 +47,29 @@ Ideally on my birthday, May 22nd! ## 🏃 Running -### 0️⃣ Project: StudentEnrollment00 +### 0️⃣1️⃣ StudentEnrollment01.InMemory + +#### AKA the in-memory event store version 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 -cd .\StudentEnrollment01 -``` - -and then build the project - -```bash +cd .\StudentEnrollment01.InMemory 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 +dotnet run --project .\StudentEnrollment01.InMemory\StudentEnrollment01.InMemory.csproj ``` -### 1️⃣ Project: StudentEnrollment01 +### 0️⃣2️⃣ StudentEnrollment02.Esdb + +#### AKA with EventStoreDB 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: @@ -83,12 +77,14 @@ While still a .NET console app, we're now going to use the Event Store database. docker-compose up ``` +**NOTE**: You will need to have ESDB running via Docker for the third (03) project as well. + 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 +cd .\StudentEnrollment02.Esdb dotnet build dotnet run ``` @@ -96,10 +92,12 @@ dotnet run Or alternatively: ```bash -dotnet run --project .\StudentEnrollment01\StudentEnrollment01.csproj +dotnet run --project .\StudentEnrollment02.Esdb\StudentEnrollment02.Esdb.csproj ``` -### 2️⃣ TBD +### 0️⃣3️⃣ StudentEnrollment03.Esdb + +#### AKA with EventStoreDB, plus a few changes Instructions will go here, but will basically be the same as above! diff --git a/StudentEnrollment01.InMemory/Dockerfile b/StudentEnrollment01.InMemory/Dockerfile new file mode 100644 index 0000000..79c55d5 --- /dev/null +++ b/StudentEnrollment01.InMemory/Dockerfile @@ -0,0 +1,21 @@ +FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base +USER $APP_UID +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["StudentEnrollment01.InMemory/StudentEnrollment01.InMemory.csproj", "StudentEnrollment01.InMemory/"] +RUN dotnet restore "StudentEnrollment01.InMemory/StudentEnrollment01.InMemory.csproj" +COPY . . +WORKDIR "/src/StudentEnrollment01.InMemory" +RUN dotnet build "StudentEnrollment01.InMemory.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "StudentEnrollment01.InMemory.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "StudentEnrollment01.InMemory.dll"] diff --git a/StudentEnrollment01.InMemory/Program.cs b/StudentEnrollment01.InMemory/Program.cs index d6a4c0c..d7f1858 100644 --- a/StudentEnrollment01.InMemory/Program.cs +++ b/StudentEnrollment01.InMemory/Program.cs @@ -22,6 +22,12 @@ "StudentId: {0}\nFullName: {1}\nEmail: {2}\nDateOfBirth: {3}\nCreatedAtUtc: {4}", student!.Id, student.FullName, student.Email, student.DateOfBirth, student.CreatedAtUtc); Console.WriteLine("Enrolled courses:"); +if (student.EnrolledCourses.Count is 0) +{ + Console.WriteLine("No enrolled courses"); + return; +} + foreach (var enrolledCourse in student.EnrolledCourses) Console.WriteLine($"\t- {enrolledCourse}"); Console.WriteLine(); \ No newline at end of file diff --git a/StudentEnrollment01.InMemory/StudentEnrollment01.InMemory.csproj b/StudentEnrollment01.InMemory/StudentEnrollment01.InMemory.csproj index e407cdc..4fa8577 100644 --- a/StudentEnrollment01.InMemory/StudentEnrollment01.InMemory.csproj +++ b/StudentEnrollment01.InMemory/StudentEnrollment01.InMemory.csproj @@ -8,4 +8,10 @@ Linux + + + .dockerignore + + + diff --git a/StudentEnrollment02.Esdb/Program.cs b/StudentEnrollment02.Esdb/Program.cs index fe4b0b7..0f02fb7 100644 --- a/StudentEnrollment02.Esdb/Program.cs +++ b/StudentEnrollment02.Esdb/Program.cs @@ -60,11 +60,14 @@ await client.AppendToStreamAsync( ); if (await streamResult.ReadState is ReadState.StreamNotFound) +{ + Console.WriteLine($"The fetched stream (id: {streamId}) that was read was in StreamNotFound state"); return; +} var eventStream = await streamResult.ToListAsync(); -Console.WriteLine("Events from selected stream: "); +Console.WriteLine($"Events (total: {eventStream.Count}) from selected stream: "); foreach (var resolved in eventStream) { Console.WriteLine($"\tEventId: {resolved.Event.EventId}"); @@ -79,6 +82,6 @@ await client.AppendToStreamAsync( .Select(re => JsonSerializer.Deserialize(re.Event.Data.ToArray())) .Select(se => se!.CourseName) .ToList(); -Console.WriteLine("Courses enrolled in: "); -enrolledCourses.ForEach(ec => Console.WriteLine($"\t- {ec}")); +Console.WriteLine("Enrolled courses found in the stream: "); +enrolledCourses.ForEach(ec => Console.WriteLine($"\t- {ec}")); // run the app multiple times ;) Console.WriteLine(""); \ No newline at end of file diff --git a/StudentEnrollment03.Esdb/Program.cs b/StudentEnrollment03.Esdb/Program.cs index f2caef9..ebb48d8 100644 --- a/StudentEnrollment03.Esdb/Program.cs +++ b/StudentEnrollment03.Esdb/Program.cs @@ -87,13 +87,15 @@ var settings = EventStoreClientSettings.Create(connectionString); var client = new EventStoreClient(settings); +// Append the initial batch of events. await client.AppendToStreamAsync( streamId, StreamState.Any, - new[] { created, enrolled, enrolled2, enrolled3, emailChanged, withdrawn }, + new[] { created, enrolled, enrolled2, enrolled3, emailChanged }, cancellationToken: default ); +// Read from the stream we just appended to. var streamResult = client.ReadStreamAsync( Direction.Forwards, streamId, @@ -101,12 +103,29 @@ await client.AppendToStreamAsync( cancellationToken: default ); +// Optional safety check, but here we're ensuring the stream was found. if (await streamResult.ReadState is ReadState.StreamNotFound) +{ + Console.WriteLine($"The fetched stream (id: {streamId}) that was read was in StreamNotFound state"); return; +} +// Okay, taking that StreamResult we're going to make it a List of ResolvedEvents. var eventStream = await streamResult.ToListAsync(); +// Get the last event's event number. +var lastEventNumFromStream = eventStream.Last().Event.EventNumber.ToUInt64(); + +// Append another event. This time let's make sure no one has appended to (AKA updated) the stream. +await client.AppendToStreamAsync( + streamId, + new StreamRevision(lastEventNumFromStream), // checking against expected revision number + new[] { withdrawn }, + cancellationToken: default +); + var student = new Student(); +Console.WriteLine($"Events (total: {eventStream.Count}) from selected stream: "); foreach (var @event in eventStream) { switch (DeserializeEvent(@event.Event)) diff --git a/docker-compose.yml b/docker-compose.yml index cea2e4a..81a92cb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,12 @@ version: "3.4" services: + app01-inmemory: + build: + dockerfile: StudentEnrollment01.InMemory/Dockerfile + context: . + container_name: app01-inmemory + student-enrollment-esdb: image: eventstore/eventstore:24.2.0-jammy environment: @@ -15,4 +21,5 @@ services: networks: - student-enrollment-esdb-net networks: - student-enrollment-esdb-net: \ No newline at end of file + student-enrollment-esdb-net: + driver: bridge \ No newline at end of file