Skip to content

Commit

Permalink
Job list es sample app (#230)
Browse files Browse the repository at this point in the history
* Started new event sourcing app.

* Added first endpoint

* Updated example with another state change

* Formatting

* Added CompleteJob API

* Update using

* Formatting

* Added some projections and read APIs

* Refactored into IJobTrackerReadService.cs

* Get all jobs lists for a user

* Refactored into Endpoints extensions.

* Renaming

* nits
  • Loading branch information
mumby0168 authored Mar 11, 2022
1 parent b1a3299 commit e77a31b
Show file tree
Hide file tree
Showing 35 changed files with 937 additions and 13 deletions.
7 changes: 7 additions & 0 deletions Microsoft.Azure.CosmosRepository.sln
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetCore", "AspNetCore",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.CosmosEventSourcingTests", "tests\Microsoft.Azure.CosmosEventSourcingTests\Microsoft.Azure.CosmosEventSourcingTests.csproj", "{FEB072AC-B573-48BF-948D-B5FFF9102303}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventSourcingJobsTracker", "samples\EventSourcingJobsTracker\EventSourcingJobsTracker.csproj", "{D276C752-4DB7-4380-AF76-91492E8F8554}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -161,6 +163,10 @@ Global
{FEB072AC-B573-48BF-948D-B5FFF9102303}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEB072AC-B573-48BF-948D-B5FFF9102303}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FEB072AC-B573-48BF-948D-B5FFF9102303}.Release|Any CPU.Build.0 = Release|Any CPU
{D276C752-4DB7-4380-AF76-91492E8F8554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D276C752-4DB7-4380-AF76-91492E8F8554}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D276C752-4DB7-4380-AF76-91492E8F8554}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D276C752-4DB7-4380-AF76-91492E8F8554}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -188,6 +194,7 @@ Global
{A57B9AC9-8CAF-47AA-9F70-EDF9B48C12A7} = {3D8EDE92-2014-4AD5-8EDD-69A2DEA33928}
{CDF603BA-5474-45E9-BBA1-2D33CAD12062} = {3D8EDE92-2014-4AD5-8EDD-69A2DEA33928}
{FEB072AC-B573-48BF-948D-B5FFF9102303} = {F8ED6752-5ED3-4EA1-89F0-363C40F8D8E0}
{D276C752-4DB7-4380-AF76-91492E8F8554} = {8F8738AD-EBC3-4C24-882B-5D9FAC427E80}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6AAE7641-B62C-48BA-8FE6-0F819E5B45EF}
Expand Down
2 changes: 1 addition & 1 deletion samples/BasicEventSourcingSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

eventSourcingBuilder.AddDomainEventTypes();
eventSourcingBuilder.AddDomainEventProjectionHandlers();
eventSourcingBuilder.AddEventItemProjectionBuilder<ShipEventItem>(options =>
eventSourcingBuilder.AddDefaultDomainEventProjectionBuilder<ShipEventItem>(options =>
{
options.ProcessorName = "shipping-demo";
options.InstanceName = Environment.MachineName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ public ShipCreatedBuilder(IRepository<ShipInformation> repository) =>
_repository = repository;

public async ValueTask HandleAsync(
ShipEvents.ShipCreated shipCreated,
ShipEvents.ShipCreated domainEvent,
ShipEventItem eventItem,
CancellationToken cancellationToken = default)
{
ShipInformation info = new(
shipCreated.Name,
shipCreated.Commissioned,
shipCreated.OccuredUtc);
domainEvent.Name,
domainEvent.Commissioned,
domainEvent.OccuredUtc);

await _repository.UpdateAsync(info, cancellationToken);
}
Expand All @@ -40,11 +40,11 @@ public ShipDockedBuilder(IRepository<ShipInformation> repository) =>
_repository = repository;

public async ValueTask HandleAsync(
ShipEvents.DockedInPort dockedInPort,
ShipEvents.DockedInPort domainEvent,
ShipEventItem eventItem,
CancellationToken cancellationToken = default)
{
(string name, string port) = dockedInPort;
(string name, string port) = domainEvent;

ShipInformation shipInfo = await _repository.GetAsync(
name,
Expand All @@ -65,11 +65,11 @@ public ShipLoadedBuilder(IRepository<ShipInformation> repository) =>
_repository = repository;

public async ValueTask HandleAsync(
ShipEvents.Loaded loaded,
ShipEvents.Loaded domainEvent,
ShipEventItem eventItem,
CancellationToken cancellationToken = default)
{
(string name, string port, double cargoWeight) = loaded;
(string name, string port, double cargoWeight) = domainEvent;

ShipInformation shipInfo = await _repository.GetAsync(
name,
Expand Down
15 changes: 15 additions & 0 deletions samples/EventSourcingJobsTracker/API/DTOs/JobDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

namespace EventSourcingJobsTracker.API.DTOs;

public record JobDto(
string Id,
string Title,
DateTime Due,
DateTime? CompletedAt = null)

{
public bool IsComplete =>
CompletedAt is not null;
}
9 changes: 9 additions & 0 deletions samples/EventSourcingJobsTracker/API/DTOs/JobsListDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

namespace EventSourcingJobsTracker.API.DTOs;

public record JobsListDto(string Id,
string Username,
string Category,
DateTime Created);
8 changes: 8 additions & 0 deletions samples/EventSourcingJobsTracker/API/Requests/CompleteJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

namespace EventSourcingJobsTracker.API.Requests;

public record CompleteJob(
Guid JobListId,
Guid JobId);
9 changes: 9 additions & 0 deletions samples/EventSourcingJobsTracker/API/Requests/CreateJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

namespace EventSourcingJobsTracker.API.Requests;

public record CreateJob(
Guid JobListId,
string Title,
DateTime Due);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

namespace EventSourcingJobsTracker.API.Requests;

public record CreateJobList(
string Name,
string Category,
string Username);
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

using EventSourcingJobsTracker.Core.Aggregates;

namespace EventSourcingJobsTracker.Application.Infrastructure;

public interface IJobListRepository
{
ValueTask SaveAsync(JobsList jobList);

ValueTask<JobsList> ReadAsync(Guid jobListId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

using EventSourcingJobsTracker.API.DTOs;
using EventSourcingJobsTracker.Infrastructure.Items;
using Microsoft.Azure.CosmosRepository;

namespace EventSourcingJobsTracker.Application.Services;

public class DefaultJobTrackerReadService : IJobTrackerReadService
{
private readonly IReadOnlyRepository<JobsListReadItem> _jobsListRepository;
private readonly IReadOnlyRepository<JobItem> _jobsRepository;

public DefaultJobTrackerReadService(
IReadOnlyRepository<JobsListReadItem> jobsListRepository,
IReadOnlyRepository<JobItem> jobsRepository)
{
_jobsListRepository = jobsListRepository;
_jobsRepository = jobsRepository;
}

public async ValueTask<JobsListDto?> FindJobsListAsync(
Guid id,
string username)
{
JobsListReadItem? jobsList = await _jobsListRepository.TryGetAsync(
id.ToString(),
username);

return jobsList is null
? null
: new JobsListDto(
jobsList.Id,
jobsList.Username,
jobsList.Category,
jobsList.CreatedTimeUtc!.Value);
}

public async ValueTask<IEnumerable<JobDto>> FindJobsForJobsListAsync(Guid jobListId)
{
IEnumerable<JobItem> jobs = await _jobsRepository.GetAsync(x =>
x.PartitionKey == jobListId.ToString());

return jobs.Select(x => new JobDto(
x.Id,
x.Title,
x.Due,
x.CompletedAt));
}

public async Task<IEnumerable<JobsListDto>> FindJobsListAsync(string username)
{
IEnumerable<JobsListReadItem> jobLists = await _jobsListRepository.GetAsync(x =>
x.PartitionKey == username);

return jobLists.Select(x => new JobsListDto(
x.Id,
x.Username,
x.Category,
x.CreatedTimeUtc!.Value));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

using EventSourcingJobsTracker.Application.Infrastructure;
using EventSourcingJobsTracker.Core.Aggregates;

namespace EventSourcingJobsTracker.Application.Services;

public class DefaultJobsTrackerService : IJobsTrackerService
{
private readonly IJobListRepository _jobListRepository;

public DefaultJobsTrackerService(IJobListRepository jobListRepository) =>
_jobListRepository = jobListRepository;

public async ValueTask<Guid> CreateJobList(
string name,
string category,
string username)
{
JobsList jobList = new(name, category, username);

await _jobListRepository.SaveAsync(jobList);

return jobList.Id;
}

public async ValueTask AddJob(
Guid jobListId,
string title,
DateTime due)
{
JobsList jobsList = await _jobListRepository.ReadAsync(jobListId);

jobsList.AddJob(title, due);

await _jobListRepository.SaveAsync(jobsList);
}

public async ValueTask CompleteJob(
Guid jobListId,
Guid jobId)
{
JobsList jobsList = await _jobListRepository.ReadAsync(jobListId);

jobsList.CompleteJob(jobId);

await _jobListRepository.SaveAsync(jobsList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

using EventSourcingJobsTracker.API.DTOs;

namespace EventSourcingJobsTracker.Application.Services;

public interface IJobTrackerReadService
{
ValueTask<JobsListDto?> FindJobsListAsync(
Guid id,
string username);

ValueTask<IEnumerable<JobDto>> FindJobsForJobsListAsync(Guid jobListId);
Task<IEnumerable<JobsListDto>> FindJobsListAsync(string username);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) IEvangelist. All rights reserved.
// Licensed under the MIT License.

namespace EventSourcingJobsTracker.Application.Services;

public interface IJobsTrackerService
{
ValueTask<Guid> CreateJobList(
string name,
string category,
string username);

ValueTask AddJob(
Guid jobListId,
string title,
DateTime due);

ValueTask CompleteJob(
Guid jobListId,
Guid jobId);
}
Loading

0 comments on commit e77a31b

Please sign in to comment.