├── .gitignore ├── Bookstore.sln ├── scripts └── api.http └── src └── Bookstore ├── ActorProviders.cs ├── Bookstore.csproj ├── ConfigurationLoader.cs ├── Controllers └── BooksController.cs ├── Domain ├── Book.cs └── BooksManagerActor.cs ├── Dto └── BookDto.cs ├── Messages ├── CreateBook.cs ├── GetBookById.cs └── GetBooks.cs ├── Program.cs ├── Properties └── launchSettings.json ├── Startup.cs ├── akka.conf ├── appsettings.Development.json └── appsettings.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.*~ 3 | project.lock.json 4 | .DS_Store 5 | *.pyc 6 | nupkg/ 7 | 8 | # Visual Studio Code 9 | .vscode 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.userosscache 15 | *.sln.docstates 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | build/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Oo]ut/ 29 | msbuild.log 30 | msbuild.err 31 | msbuild.wrn 32 | 33 | # Visual Studio 2015 34 | .vs/ 35 | 36 | .idea 37 | -------------------------------------------------------------------------------- /Bookstore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2016 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bookstore", "src\Bookstore\Bookstore.csproj", "{F247CF31-A0E1-43E2-9B00-CD6CF02C3F6C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F247CF31-A0E1-43E2-9B00-CD6CF02C3F6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F247CF31-A0E1-43E2-9B00-CD6CF02C3F6C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F247CF31-A0E1-43E2-9B00-CD6CF02C3F6C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F247CF31-A0E1-43E2-9B00-CD6CF02C3F6C}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {83F3F576-DBE3-4BBD-840F-EBB58D72F9EE} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /scripts/api.http: -------------------------------------------------------------------------------- 1 | @api = https://localhost:5001 2 | @bookId = 29301472-d042-4dc5-bd4f-1b37a469bd18 3 | 4 | ### Get book 5 | GET {{api}}/api/books/{{bookId}} 6 | 7 | ### Get books 8 | GET {{api}}/api/books 9 | 10 | ### Create new book 11 | POST {{api}}/api/books 12 | content-type: application/json 13 | 14 | { 15 | "title": "Domain-driven design", 16 | "author": "Eric J. Evans", 17 | "cost": 500, 18 | "inventoryAmount": 20 19 | } -------------------------------------------------------------------------------- /src/Bookstore/ActorProviders.cs: -------------------------------------------------------------------------------- 1 | using Akka.Actor; 2 | 3 | namespace Bookstore 4 | { 5 | public delegate IActorRef BooksManagerActorProvider(); 6 | } -------------------------------------------------------------------------------- /src/Bookstore/Bookstore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Bookstore/ConfigurationLoader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Akka.Configuration; 3 | 4 | namespace Bookstore 5 | { 6 | public class ConfigurationLoader 7 | { 8 | public static Config Load() => LoadConfig("akka.conf"); 9 | 10 | private static Config LoadConfig(string configFile) 11 | { 12 | if (File.Exists(configFile)) 13 | { 14 | string config = File.ReadAllText(configFile); 15 | return ConfigurationFactory.ParseString(config); 16 | } 17 | 18 | 19 | return Config.Empty; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Bookstore/Controllers/BooksController.cs: -------------------------------------------------------------------------------- 1 | using Akka.Actor; 2 | using Bookstore.Dto; 3 | using Bookstore.Messages; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | 9 | namespace Bookstore.Controllers 10 | { 11 | [Route("api/[controller]")] 12 | [ApiController] 13 | public class BooksController : ControllerBase 14 | { 15 | private readonly IActorRef _booksManagerActor; 16 | 17 | public BooksController(BooksManagerActorProvider booksManagerActorProvider) 18 | { 19 | _booksManagerActor = booksManagerActorProvider(); 20 | } 21 | 22 | [HttpGet] 23 | public async Task Get() 24 | { 25 | var books = await _booksManagerActor.Ask>(GetBooks.Instance); 26 | return Ok(books); 27 | } 28 | 29 | [HttpGet("{id}")] 30 | public async Task Get(Guid id) 31 | { 32 | var result = await _booksManagerActor.Ask(new GetBookById(id)); 33 | switch (result) 34 | { 35 | case BookDto book: 36 | return Ok(book); 37 | default: 38 | return BadRequest(); 39 | } 40 | } 41 | 42 | [HttpPost] 43 | public IActionResult Post([FromBody] CreateBook command) 44 | { 45 | _booksManagerActor.Tell(command); 46 | return Accepted(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Bookstore/Domain/Book.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Bookstore.Domain 4 | { 5 | public class Book 6 | { 7 | public Guid Id { get; set; } 8 | public string Title { get; set; } 9 | public string Author { get; set; } 10 | public decimal Cost { get; set; } 11 | public int InventoryAmount { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Bookstore/Domain/BooksManagerActor.cs: -------------------------------------------------------------------------------- 1 | using Akka.Actor; 2 | using Bookstore.Dto; 3 | using Bookstore.Messages; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Bookstore.Domain 9 | { 10 | public class BooksManagerActor : ReceiveActor 11 | { 12 | private readonly Dictionary _books = new Dictionary(); 13 | 14 | public BooksManagerActor() 15 | { 16 | Receive(command => 17 | { 18 | var newBook = new Book 19 | { 20 | Id = Guid.NewGuid(), 21 | Title = command.Title, 22 | Author = command.Author, 23 | Cost = command.Cost, 24 | InventoryAmount = command.InventoryAmount, 25 | }; 26 | 27 | _books.Add(newBook.Id, newBook); 28 | }); 29 | 30 | Receive(query => 31 | { 32 | if (_books.TryGetValue(query.Id, out var book)) 33 | Sender.Tell(GetBookDto(book)); 34 | else 35 | Sender.Tell(BookNotFound.Instance); 36 | }); 37 | 38 | Receive(query => 39 | Sender.Tell(_books.Select(x => GetBookDto(x.Value)).ToList())); 40 | } 41 | 42 | private static BookDto GetBookDto(Book book) => new BookDto 43 | { 44 | Id = book.Id, 45 | Title = book.Title, 46 | Author = book.Author, 47 | Cost = book.Cost, 48 | InventoryAmount = book.InventoryAmount 49 | }; 50 | } 51 | } -------------------------------------------------------------------------------- /src/Bookstore/Dto/BookDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Bookstore.Dto 4 | { 5 | public class BookDto 6 | { 7 | public Guid Id { get; set; } 8 | public string Title { get; set; } 9 | public string Author { get; set; } 10 | public decimal Cost { get; set; } 11 | public int InventoryAmount { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Bookstore/Messages/CreateBook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Bookstore.Messages 4 | { 5 | public class CreateBook 6 | { 7 | public CreateBook(string title, string author, decimal cost, int inventoryAmount) 8 | { 9 | Title = title; 10 | Author = author; 11 | Cost = cost; 12 | InventoryAmount = inventoryAmount; 13 | } 14 | 15 | public string Title { get; } 16 | public string Author { get; } 17 | public decimal Cost { get; } 18 | public int InventoryAmount { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Bookstore/Messages/GetBookById.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Bookstore.Messages 4 | { 5 | public class GetBookById 6 | { 7 | public GetBookById(Guid id) 8 | { 9 | Id = id; 10 | } 11 | 12 | public Guid Id { get; } 13 | } 14 | 15 | public class BookNotFound 16 | { 17 | private BookNotFound() { } 18 | public static BookNotFound Instance { get; } = new BookNotFound(); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/Bookstore/Messages/GetBooks.cs: -------------------------------------------------------------------------------- 1 | namespace Bookstore.Messages 2 | { 3 | public class GetBooks 4 | { 5 | private GetBooks() { } 6 | public static GetBooks Instance { get; } = new GetBooks(); 7 | } 8 | } -------------------------------------------------------------------------------- /src/Bookstore/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Bookstore 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Bookstore/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "Bookstore": { 5 | "commandName": "Project", 6 | "launchBrowser": true, 7 | "launchUrl": "api/books", 8 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development" 11 | } 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Bookstore/Startup.cs: -------------------------------------------------------------------------------- 1 | using Akka.Actor; 2 | using Bookstore.Domain; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | 9 | namespace Bookstore 10 | { 11 | public class Startup 12 | { 13 | public Startup(IConfiguration configuration) 14 | { 15 | Configuration = configuration; 16 | } 17 | 18 | public IConfiguration Configuration { get; } 19 | 20 | // This method gets called by the runtime. Use this method to add services to the container. 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 24 | 25 | // Register ActorSystem 26 | services.AddSingleton(_ => ActorSystem.Create("bookstore", ConfigurationLoader.Load())); 27 | 28 | services.AddSingleton(provider => 29 | { 30 | var actorSystem = provider.GetService(); 31 | var booksManagerActor = actorSystem.ActorOf(Props.Create(() => new BooksManagerActor())); 32 | return () => booksManagerActor; 33 | }); 34 | } 35 | 36 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 37 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime) 38 | { 39 | if (env.IsDevelopment()) 40 | { 41 | app.UseDeveloperExceptionPage(); 42 | } 43 | else 44 | { 45 | app.UseHsts(); 46 | } 47 | 48 | app.UseHttpsRedirection(); 49 | app.UseMvc(); 50 | 51 | lifetime.ApplicationStarted.Register(() => 52 | { 53 | app.ApplicationServices.GetService(); // start Akka.NET 54 | }); 55 | lifetime.ApplicationStopping.Register(() => 56 | { 57 | app.ApplicationServices.GetService().Terminate().Wait(); 58 | }); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Bookstore/akka.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | loglevel = info 3 | } -------------------------------------------------------------------------------- /src/Bookstore/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Bookstore/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | --------------------------------------------------------------------------------