├── .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 |
--------------------------------------------------------------------------------