Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81d8a76256 | ||
|
|
3a414dbf79 | ||
|
|
19b1c4295e | ||
|
|
cd9bb683c8 | ||
|
|
8a58fa6d34 | ||
|
|
af0f787f6a | ||
|
|
ce55972d64 | ||
|
|
6d3db24188 | ||
|
|
63b81a62e4 | ||
|
|
16bc48a491 | ||
|
|
be322e581b | ||
|
|
884c3a6f34 | ||
|
|
261c037f27 | ||
|
|
457285e194 | ||
|
|
cb274449a8 | ||
|
|
9628a4f60b | ||
|
|
dc690a49f0 | ||
|
|
efaeedce3f | ||
|
|
9231f5a9a6 | ||
|
|
d25db6a360 | ||
|
|
472185f752 | ||
|
|
92287837ec | ||
|
|
fe2d38cd12 | ||
|
|
be6b993d03 | ||
|
|
c5551d84fa | ||
|
|
c3c5e3a729 | ||
|
|
86777211a4 | ||
|
|
a3c98f8bd7 | ||
|
|
fcbd1a7294 | ||
|
|
92570ff64d | ||
|
|
4bf8d9d583 | ||
|
|
4a353a8baf | ||
|
|
3271a92164 | ||
|
|
d721e13108 | ||
|
|
46db1f45ac | ||
|
|
5ef48012fb | ||
|
|
1241a58f3a | ||
|
|
5a10794876 | ||
|
|
a003da0240 | ||
|
|
b21559bd0d | ||
|
|
f99dcce2e0 | ||
|
|
ed3afddf21 | ||
|
|
847c3ee696 | ||
|
|
7175f13b21 | ||
|
|
644bcf0294 | ||
|
|
d409c29c73 | ||
|
|
2b691e61bf | ||
|
|
d0595f758e | ||
|
|
4160881df9 | ||
|
|
a426a1a3a4 |
80
README.md
@@ -1,7 +1,54 @@
|
||||
Neste projeto experimental em Domain-Driven-Design com implementação de Aggregates + Event Sourcing + CQRS + Optimistic Concurrency a técnica de Event Sourcing é usada como ferramenta de auditoria das modificações do conteúdo do Blog. A fonte dos dados são os Domain Events registrados no Kafka, para permitir consultas de alta performance, foi criado um banco de dados MongoDB com o 'último estado já processado dos eventos'. Há um microsserviço auxiliar de autenticação. Tudo isso foi implementado em .NET Core/Standard compatível com Docker! Divirta-se!
|
||||
A solution for Blogging based on a Event-Driven architecture with DDD and CQRS. The full solution contains three applications.
|
||||
* A Web API which receives Commands to produces Domain Events also receives Queries to return JSON.
|
||||
* A Consumer App that reads the Event Stream and do a projection to a MongoDB database.
|
||||
* A Web API for authentication and JWT generation.
|
||||
|
||||
#### O Domínio
|
||||

|
||||
#### Requirements
|
||||
* [Visual Studio 2017 + Update 3](https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes)
|
||||
* [.NET SDK 2.0](https://www.microsoft.com/net/download/core)
|
||||
* [Docker](https://docs.docker.com/docker-for-windows/install/)
|
||||
|
||||
#### Environment setup
|
||||
|
||||
*If you already have valid connections strings for Kafka and MongoDB you could skip this topic and go to the Running the applications topic.*
|
||||
|
||||
* Run the `./up-kafka-mongodb.sh` script to run Kafka and MongoDB as Docker Containers. Please wait until the ~800mb download to be complete.
|
||||
|
||||
```
|
||||
$ ./up-kafka-mongodb.sh
|
||||
Pulling mongodb (mongo:latest)...
|
||||
latest: Pulling from library/mongo
|
||||
Digest: sha256:2c55bcc870c269771aeade05fc3dd3657800540e0a48755876a1dc70db1e76d9
|
||||
Status: Downloaded newer image for mongo:latest
|
||||
Pulling kafka (spotify/kafka:latest)...
|
||||
latest: Pulling from spotify/kafka
|
||||
Digest: sha256:cf8f8f760b48a07fb99df24fab8201ec8b647634751e842b67103a25a388981b
|
||||
Status: Downloaded newer image for spotify/kafka:latest
|
||||
Creating setup_mongodb_1 ...
|
||||
Creating setup_kafka_1 ...
|
||||
Creating setup_mongodb_1
|
||||
Creating setup_mongodb_1 ... done
|
||||
```
|
||||
* Check if the data layer is ready with the following commands:
|
||||
|
||||
```
|
||||
$ docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
mongo latest d22888af0ce0 17 hours ago 361MB
|
||||
spotify/kafka latest a9e0a5b8b15e 11 months ago 443MB
|
||||
```
|
||||
|
||||
```
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
32452776153f spotify/kafka "supervisord -n" 2 days ago Up 2 days 0.0.0.0:2181->2181/tcp, 0.0.0.0:9092->9092/tcp setup_kafka_1
|
||||
ba28cf144478 mongo "docker-entrypoint..." 2 days ago Up 2 days 0.0.0.0:27017->27017/tcp setup_mongodb_1
|
||||
```
|
||||
|
||||
If Kafka is running good it will be working with the `10.0.75.1:9092` connection string and if MongoDB is running good it will be working at `mongodb://10.0.75.1:27017`.
|
||||
|
||||
#### The Domain
|
||||

|
||||
|
||||
#### As Aplicações desta Solução
|
||||
* **Producer**: Web API que recebe os comandos de edição de conteúdo, produz Eventos de Domínio e publica as mensagens em um tópico no Kafka.
|
||||
@@ -15,7 +62,7 @@ Há duas formas de iniciar a solução.
|
||||
|
||||
Resolver os [pré-requisitos](https://github.com/ivanpaulovich/jambo/#prerequisitos), definir o projeto inicial como sendo o `docker-compose` e então apertar `Ctrl+F5` para executar todas as aplicações. Se tudo estiver correto, digite `docker ps` no seu terminal para verificar em quais portas cada aplicação está executando. Será algo assim:
|
||||
|
||||

|
||||

|
||||
|
||||
A partir daí basta acessar:
|
||||
* Auth em http://localhost:32775/swagger/
|
||||
@@ -28,24 +75,30 @@ Leia o [o jeito não tão fácil](https://github.com/ivanpaulovich/jambo/#2-o-je
|
||||
A outra opção é inicializar aplicação por aplicação, seguindo o seguintes passos:
|
||||
|
||||
1. Execute o projeto **Jambo.Auth.WebAPI** e chame o método *Account/Token* com qualquer usuário e senha. *Guarde este token*.
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
3. Execute o projeto **Jambo.Producer.WebAPI** e clique no botão *Authorization* (topo direito da página).
|
||||
|
||||
Digite `bearer + valor_do_token` e clique em fechar. Algo assim:
|
||||

|
||||

|
||||
Chame os métodos para manutenção dos dados do Blog, Posts e Comentários.
|
||||

|
||||

|
||||
|
||||
2. Execute o projeto **Jambo.Consumer.Console** e garante que ele **contínua em execução**.
|
||||

|
||||
|
||||

|
||||
|
||||
4. Visualize suas modificações
|
||||

|
||||
|
||||

|
||||
|
||||
#### Demo
|
||||
*Em breve...*
|
||||
* **Auth API**: http://jambo.westus.cloudapp.azure.com:7070/swagger/.
|
||||
* **Producer**: http://jambo.westus.cloudapp.azure.com:7080/swagger/.
|
||||
* **Consumer**: Executa em background neste servidor.
|
||||
|
||||
#### Próximos passos?
|
||||
1. Publicar os containers no Azure.
|
||||
@@ -54,6 +107,7 @@ Chame os métodos para manutenção dos dados do Blog, Posts e Comentários.
|
||||
4. Consumir serviços externos.
|
||||
5. Implementação alternativa de barramento: Azure Event Hubs
|
||||
6. Implementação alternativa de snapshot: Azure Cosmos DB
|
||||
7. Implementar um HealthCheck
|
||||
|
||||
#### Pré-requisitos
|
||||
|
||||
@@ -74,5 +128,5 @@ Obrigado de verdade!
|
||||
Agradeço todo comentário sobre o projeto. Envie suas dúvidas e sugestões no [Fórum](https://github.com/ivanpaulovich/jambo/issues).
|
||||
|
||||
#### Histórico de Versões
|
||||
* 12/set/2017:
|
||||
*Em breve...*
|
||||
* 10/set/2017:
|
||||
[](https://github.com/ivanpaulovich/jambo/releases/latest)
|
||||
|
||||
1
docker-demo/demo-down.sh
Normal file
@@ -0,0 +1 @@
|
||||
docker-compose down
|
||||
1
docker-demo/demo-up.sh
Normal file
@@ -0,0 +1 @@
|
||||
docker-compose up -d
|
||||
@@ -1,31 +1,42 @@
|
||||
version: '3'
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
jambo.auth.webapi:
|
||||
image: ivanpaulovich/jambo:auth.webapi
|
||||
build:
|
||||
context: ./Jambo.Auth.WebAPI
|
||||
dockerfile: Dockerfile
|
||||
links:
|
||||
- "mongodb:mongo"
|
||||
depends_on:
|
||||
- mongodb
|
||||
ports:
|
||||
- 7070:80
|
||||
jambo.producer.webapi:
|
||||
image: ivanpaulovich/jambo:producer.webapi
|
||||
build:
|
||||
context: ./Jambo.Producer.WebAPI
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 7080:80
|
||||
links:
|
||||
- "mongodb:mongo"
|
||||
depends_on:
|
||||
- mongodb
|
||||
- kafka
|
||||
environment:
|
||||
- ASPNETCORE_URLS=http://*:80
|
||||
- ServiceBus__ConnectionString=kafka:9092
|
||||
- ServiceBus__Topic=jambov32
|
||||
- MongoDB__ConnectionString=mongodb://mongodb:27017
|
||||
- MongoDB__Database=jambov32
|
||||
jambo.consumer.console:
|
||||
image: ivanpaulovich/jambo:consumer.console
|
||||
build:
|
||||
context: ./Jambo.Consumer.Console
|
||||
dockerfile: Dockerfile
|
||||
links:
|
||||
- "mongodb:mongo"
|
||||
depends_on:
|
||||
- mongodb
|
||||
- kafka
|
||||
environment:
|
||||
- ASPNETCORE_URLS=http://*:80
|
||||
- ServiceBus__ConnectionString=kafka:9092
|
||||
- ServiceBus__Topic=jambov32
|
||||
- MongoDB__ConnectionString=mongodb://mongodb:27017
|
||||
- MongoDB__Database=jambov32
|
||||
mongodb:
|
||||
image: mongo
|
||||
container_name: "mongodb"
|
||||
@@ -40,4 +51,4 @@ services:
|
||||
- 9581-9585:9581-9585
|
||||
- 9092:9092
|
||||
environment:
|
||||
- ADV_HOST=10.0.75.1
|
||||
- ADV_HOST=kafka
|
||||
BIN
docs/Domain.pptx
1
docs/_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-cayman
|
||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@@ -1,14 +0,0 @@
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Jambo.Application.DomainEventHandlers
|
||||
{
|
||||
public interface IEvent<T> : IRequest<T>
|
||||
where T : DomainEvent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,11 @@ namespace Jambo.Auth.Application.Commands
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Username { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Password { get; private set; }
|
||||
public Guid SchoolId { get; private set; }
|
||||
|
||||
public LoginCommand()
|
||||
{
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="CommandHandlers\**" />
|
||||
<EmbeddedResource Remove="CommandHandlers\**" />
|
||||
<None Remove="CommandHandlers\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mediatr" Version="3.0.1" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="CommandHandlers\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -3,4 +3,4 @@ ARG source
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
COPY ${source:-obj/Docker/publish} .
|
||||
ENTRYPOINT ["dotnet", "Jambo.Producer.WebAPI.dll"]
|
||||
ENTRYPOINT ["dotnet", "Jambo.Auth.Infrastructure.dll"]
|
||||
@@ -2,24 +2,16 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Auth.Application\Jambo.Auth.Application.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Auth.UI\Jambo.Auth.UI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jambo.Producer.WebAPI
|
||||
namespace Jambo.Auth.Infrastructure
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
@@ -3,7 +3,7 @@
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:8546/",
|
||||
"applicationUrl": "http://localhost:15877/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
@@ -16,14 +16,14 @@
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Jambo.Auth.WebAPI": {
|
||||
"Jambo.Auth.Infrastructure": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "api/values",
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:8547/"
|
||||
"applicationUrl": "http://localhost:15878/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
source/Jambo.Auth.Infrastructure/Startup.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Jambo.Auth.Infrastructure
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc();
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.DescribeAllEnumsAsStrings();
|
||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
|
||||
{
|
||||
Title = "Auth API",
|
||||
Version = "v1",
|
||||
Description = "The Auth API",
|
||||
TermsOfService = "Terms Of Service"
|
||||
});
|
||||
});
|
||||
|
||||
services.AddScoped(
|
||||
s => new UI.Config(Configuration.GetSection("Security").GetValue<string>("SecretKey"),
|
||||
Configuration.GetSection("Security").GetValue<string>("Issuer")));
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseMvc();
|
||||
|
||||
app.UseSwagger()
|
||||
.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Jambo.Auth.IoC
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Jambo.Auth.Application.Auth
|
||||
namespace Jambo.Auth.UI
|
||||
{
|
||||
public class Config
|
||||
{
|
||||
@@ -8,10 +8,9 @@ using System.IdentityModel.Tokens.Jwt;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Jambo.Auth.Application.Auth;
|
||||
using Jambo.Auth.Application.Commands;
|
||||
|
||||
namespace Jambo.Auth.WebAPI.Controllers
|
||||
namespace Jambo.Auth.UI
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
public class AccountController : Controller
|
||||
@@ -27,7 +26,12 @@ namespace Jambo.Auth.WebAPI.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult Token([FromBody] LoginCommand loginCommand)
|
||||
{
|
||||
var token = GetJwtSecurityToken(loginCommand.Username);
|
||||
//
|
||||
// TODO: Implementar uma verificação
|
||||
// se loginCommand.Username e loginCommand.Password são válidos.
|
||||
//
|
||||
|
||||
var token = GetJwtSecurityToken(loginCommand);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
@@ -36,25 +40,26 @@ namespace Jambo.Auth.WebAPI.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
private JwtSecurityToken GetJwtSecurityToken(string user)
|
||||
private JwtSecurityToken GetJwtSecurityToken(LoginCommand user)
|
||||
{
|
||||
return new JwtSecurityToken(
|
||||
config.Issuer,
|
||||
config.Issuer,
|
||||
GetTokenClaims(user),
|
||||
expires: DateTime.UtcNow.AddMinutes(30),
|
||||
expires: DateTime.UtcNow.AddDays(1),
|
||||
signingCredentials: new SigningCredentials(
|
||||
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.SecretKey)),
|
||||
SecurityAlgorithms.HmacSha256)
|
||||
);
|
||||
}
|
||||
|
||||
private static IEnumerable<Claim> GetTokenClaims(string user)
|
||||
private static IEnumerable<Claim> GetTokenClaims(LoginCommand user)
|
||||
{
|
||||
return new List<Claim>
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user)
|
||||
new Claim(JwtRegisteredClaimNames.Jti, user.UserId.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user.UserId.ToString()),
|
||||
new Claim(ClaimTypes.GroupSid, user.SchoolId.ToString()),
|
||||
};
|
||||
}
|
||||
}
|
||||
16
source/Jambo.Auth.UI/Jambo.Auth.UI.csproj
Normal file
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Auth.Application\Jambo.Auth.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jambo.Auth.WebAPI
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildWebHost(args).Run();
|
||||
}
|
||||
|
||||
public static IWebHost BuildWebHost(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
using Jambo.Auth.Application.Auth;
|
||||
|
||||
namespace Jambo.Auth.WebAPI
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc();
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.DescribeAllEnumsAsStrings();
|
||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
|
||||
{
|
||||
Title = "Auth API",
|
||||
Version = "v1",
|
||||
Description = "The Auth API",
|
||||
TermsOfService = "Terms Of Service"
|
||||
});
|
||||
});
|
||||
|
||||
services.AddScoped(
|
||||
s => new Config(Configuration.GetSection("Security").GetValue<string>("SecretKey"),
|
||||
Configuration.GetSection("Security").GetValue<string>("Issuer")));
|
||||
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.SaveToken = true;
|
||||
options.RequireHttpsMetadata = false;
|
||||
options.TokenValidationParameters = new TokenValidationParameters()
|
||||
{
|
||||
ValidIssuer = Configuration.GetSection("Security").GetValue<string>("Issuer"),
|
||||
ValidAudience = Configuration.GetSection("Security").GetValue<string>("Issuer"),
|
||||
IssuerSigningKey = new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(
|
||||
Configuration.GetSection("Security").GetValue<string>("SecretKey")))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseMvc();
|
||||
|
||||
app.UseSwagger()
|
||||
.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
37
source/Jambo.Auth.sln
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Auth.Application", "Jambo.Auth.Application\Jambo.Auth.Application.csproj", "{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jambo.Auth.Infrastructure", "Jambo.Auth.Infrastructure\Jambo.Auth.Infrastructure.csproj", "{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jambo.Auth.UI", "Jambo.Auth.UI\Jambo.Auth.UI.csproj", "{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {54AE4BA0-6081-4892-A9B9-3F8D15D994C8}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"MongoDB": {
|
||||
"ConnectionString": "mongodb://10.0.75.1:27017",
|
||||
"Database": "jambov32"
|
||||
},
|
||||
|
||||
"ServiceBus": {
|
||||
"ConnectionString": "10.0.75.1:9092",
|
||||
"Topic": "jambov32"
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Jambo.Domain.Model.Posts;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace Jambo.Infrastructure
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess
|
||||
{
|
||||
public class MongoContext
|
||||
{
|
||||
@@ -20,7 +20,7 @@ namespace Jambo.Infrastructure
|
||||
|
||||
public void DatabaseReset(string databaseName)
|
||||
{
|
||||
mongoClient.DropDatabase(databaseName);
|
||||
mongoClient.DropDatabase(databaseName);
|
||||
}
|
||||
|
||||
public IMongoCollection<Blog> Blogs
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Infrastructure.Repositories.Blogs
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs
|
||||
{
|
||||
public class BlogReadOnlyRepository : IBlogReadOnlyRepository
|
||||
{
|
||||
@@ -3,7 +3,7 @@ using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Infrastructure.Repositories.Blogs
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs
|
||||
{
|
||||
public class BlogWriteOnlyRepository : IBlogWriteOnlyRepository
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Infrastructure.Repositories.Posts
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts
|
||||
{
|
||||
public class PostReadOnlyRepository : IPostReadOnlyRepository
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Infrastructure.Repositories.Posts
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts
|
||||
{
|
||||
public class PostWriteOnlyRepository : IPostWriteOnlyRepository
|
||||
{
|
||||
@@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;1705;NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.6.0" />
|
||||
<PackageReference Include="Autofac.Configuration" Version="4.0.1" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.0" />
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
<PackageReference Include="Confluent.Kafka" Version="0.11.0" NoWarn="NU1701" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyFiles" AfterTargets="build">
|
||||
<Copy DestinationFolder="..\Jambo.Consumer.UI\bin\Debug\netcoreapp2.0" SourceFiles="$(OutputPath)\Jambo.Consumer.Infrastructure.dll" SkipUnchangedFiles="false" />
|
||||
<Copy DestinationFolder="..\Jambo.Consumer.UI\bin\Debug\netcoreapp2.0" SourceFiles="$(OutputPath)\Jambo.Consumer.Infrastructure.pdb" SkipUnchangedFiles="false" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,28 +1,21 @@
|
||||
using Autofac;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.Infrastructure;
|
||||
using Jambo.Infrastructure.Repositories;
|
||||
using Jambo.Infrastructure.Repositories.Blogs;
|
||||
using Jambo.Infrastructure.Repositories.Posts;
|
||||
|
||||
namespace Jambo.Consumer.IoC
|
||||
namespace Jambo.Consumer.Infrastructure.Modules
|
||||
{
|
||||
using Autofac;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs;
|
||||
using Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts;
|
||||
using Jambo.Consumer.Infrastructure.DataAccess;
|
||||
|
||||
public class ApplicationModule : Module
|
||||
{
|
||||
public readonly string connectionString;
|
||||
public readonly string database;
|
||||
|
||||
public ApplicationModule(string connectionString, string database)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
this.database = database;
|
||||
}
|
||||
public string ConnectionString { get; set; }
|
||||
public string DatabaseName { get; set; }
|
||||
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
MongoContext mongoContext = new MongoContext(connectionString, database);
|
||||
mongoContext.DatabaseReset(database);
|
||||
MongoContext mongoContext = new MongoContext(ConnectionString, DatabaseName);
|
||||
mongoContext.DatabaseReset(DatabaseName);
|
||||
|
||||
builder.Register(c => mongoContext)
|
||||
.As<MongoContext>().SingleInstance();
|
||||
21
source/Jambo.Consumer.Infrastructure/Modules/BusModule.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace Jambo.Consumer.Infrastructure.Modules
|
||||
{
|
||||
using Autofac;
|
||||
using Jambo.Consumer.Infrastructure.ServiceBus;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
public class BusModule : Module
|
||||
{
|
||||
public string BrokerList { get; set; }
|
||||
public string Topic { get; set; }
|
||||
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.RegisterType<Bus>()
|
||||
.As<ISubscriber>()
|
||||
.WithParameter("brokerList", BrokerList)
|
||||
.WithParameter("topic", Topic)
|
||||
.SingleInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.IO;
|
||||
|
||||
namespace Jambo.Consumer.Console
|
||||
namespace Jambo.Consumer.Infrastructure
|
||||
{
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.IO;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
@@ -14,7 +14,6 @@ namespace Jambo.Consumer.Console
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
IConfigurationRoot configuration = builder.Build();
|
||||
|
||||
IServiceCollection serviceCollection = new ServiceCollection();
|
||||
|
||||
Startup startup = new Startup(configuration);
|
||||
@@ -2,6 +2,7 @@
|
||||
using Confluent.Kafka.Serialization;
|
||||
using Jambo.Domain.Exceptions;
|
||||
using Jambo.Domain.Model;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
@@ -9,54 +10,42 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.ServiceBus.Kafka
|
||||
namespace Jambo.Consumer.Infrastructure.ServiceBus
|
||||
{
|
||||
public class Bus : IPublisher, ISubscriber
|
||||
public class Bus : ISubscriber
|
||||
{
|
||||
private readonly Config config;
|
||||
public readonly string brokerList;
|
||||
public readonly string topic;
|
||||
|
||||
private readonly Producer<string, string> _producer;
|
||||
private readonly Consumer<string, string> _consumer;
|
||||
private readonly Consumer<string, string> consumer;
|
||||
|
||||
public Bus(Config config)
|
||||
public Bus(string brokerList, string topic)
|
||||
{
|
||||
this.config = config;
|
||||
this.brokerList = brokerList;
|
||||
this.topic = topic;
|
||||
|
||||
_producer = new Producer<string, string>(
|
||||
consumer = new Consumer<string, string>(
|
||||
new Dictionary<string, object>()
|
||||
{{
|
||||
"bootstrap.servers", config.BrokerList}},
|
||||
new StringSerializer(Encoding.UTF8), new StringSerializer(Encoding.UTF8));
|
||||
|
||||
_consumer = new Consumer<string, string>(
|
||||
new Dictionary<string, object>()
|
||||
{{ "group.id", "simple-csharp-consumer" },
|
||||
{ "bootstrap.servers", config.BrokerList }},
|
||||
new StringDeserializer(Encoding.UTF8), new StringDeserializer(Encoding.UTF8));
|
||||
}
|
||||
|
||||
public async Task Publish(DomainEvent domainEvent)
|
||||
{
|
||||
string data = JsonConvert.SerializeObject(domainEvent, Formatting.Indented);
|
||||
|
||||
Message<string, string> message = await _producer.ProduceAsync(
|
||||
config.TopicName, domainEvent.GetType().AssemblyQualifiedName, data);
|
||||
{
|
||||
{ "group.id", "consumer" },
|
||||
{ "bootstrap.servers", brokerList }
|
||||
}, new StringDeserializer(Encoding.UTF8), new StringDeserializer(Encoding.UTF8));
|
||||
}
|
||||
|
||||
public void Listen(IMediator mediator)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
_consumer.Assign(new List<TopicPartitionOffset>
|
||||
consumer.Assign(new List<TopicPartitionOffset>
|
||||
{
|
||||
new TopicPartitionOffset(config.TopicName, 0, 0)
|
||||
new TopicPartitionOffset(topic, 0, 0)
|
||||
});
|
||||
|
||||
while (true)
|
||||
{
|
||||
Message<string, string> msg;
|
||||
|
||||
if (_consumer.Consume(out msg, TimeSpan.FromSeconds(1)))
|
||||
if (consumer.Consume(out msg, TimeSpan.FromSeconds(1)))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -74,7 +63,7 @@ namespace Jambo.ServiceBus.Kafka
|
||||
|
||||
mediator.Send(domainEvent).Wait();
|
||||
}
|
||||
catch (BlogDomainException ex)
|
||||
catch (DomainException ex)
|
||||
{
|
||||
Console.WriteLine(ex.BusinessMessage);
|
||||
}
|
||||
@@ -93,17 +82,7 @@ namespace Jambo.ServiceBus.Kafka
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_producer.Dispose();
|
||||
_consumer.Dispose();
|
||||
}
|
||||
|
||||
public async Task Publish(IEnumerable<DomainEvent> domainEvents, Header header)
|
||||
{
|
||||
foreach (var domainEvent in domainEvents)
|
||||
{
|
||||
domainEvent.SetHeader(header);
|
||||
await Publish(domainEvent);
|
||||
}
|
||||
consumer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Jambo.ServiceBus;
|
||||
using Newtonsoft.Json;
|
||||
using MediatR;
|
||||
using System.Reflection;
|
||||
using Autofac;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Jambo.Consumer.IoC;
|
||||
using System.Threading;
|
||||
using Jambo.Consumer.Application.DomainEventHandlers.Blogs;
|
||||
using Jambo.Domain.Model;
|
||||
using Jambo.Consumer.Infrastructure.Modules;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
namespace Jambo.Consumer.Console
|
||||
namespace Jambo.Consumer.Infrastructure
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
@@ -57,7 +52,6 @@ namespace Jambo.Consumer.Console
|
||||
|
||||
while (true)
|
||||
{
|
||||
System.Console.WriteLine(DateTime.Now.ToString());
|
||||
Thread.Sleep(1000 * 60);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Autofac;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.Infrastructure;
|
||||
using Jambo.ServiceBus.Kafka;
|
||||
using MediatR;
|
||||
|
||||
namespace Jambo.Consumer.IoC
|
||||
{
|
||||
public class BusModule : Module
|
||||
{
|
||||
private readonly string connectionString;
|
||||
private readonly string topic;
|
||||
|
||||
public BusModule(string connectionString, string topic)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.Register(c => new Config(connectionString, topic)).As<Config>().SingleInstance();
|
||||
builder.RegisterType<Bus>().As<ISubscriber>().SingleInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;1705;NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.6.0" />
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Infrastructure\Jambo.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus.Kafka\Jambo.ServiceBus.Kafka.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -3,4 +3,4 @@ ARG source
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
COPY ${source:-obj/Docker/publish} .
|
||||
ENTRYPOINT ["dotnet", "Jambo.Consumer.Console.dll"]
|
||||
ENTRYPOINT ["dotnet", "Jambo.Consumer.UI.dll"]
|
||||
@@ -10,35 +10,25 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.6.0" />
|
||||
<PackageReference Include="Autofac.Configuration" Version="4.0.1" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.0" />
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
<PackageReference Include="Confluent.Kafka" Version="0.11.0" NoWarn="NU1701" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="System.ComponentModel" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Consumer.IoC\Jambo.Consumer.IoC.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update=".dockerignore">
|
||||
<DependentUpon>Dockerfile</DependentUpon>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
25
source/Jambo.Consumer.UI/Program.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Jambo.Consumer.Infrastructure
|
||||
{
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.IO;
|
||||
|
||||
class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.AddJsonFile("autofac.json")
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
IConfigurationRoot configuration = builder.Build();
|
||||
IServiceCollection serviceCollection = new ServiceCollection();
|
||||
|
||||
Startup startup = new Startup(configuration);
|
||||
startup.ConfigureServices(serviceCollection);
|
||||
startup.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
69
source/Jambo.Consumer.UI/Startup.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
namespace Jambo.Consumer.Infrastructure
|
||||
{
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Loader;
|
||||
using Autofac.Configuration;
|
||||
using Jambo.Consumer.Application.DomainEventHandlers.Blogs;
|
||||
|
||||
public class Startup
|
||||
{
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
IServiceProvider serviceProvider;
|
||||
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
LoadInfrastructureAssemblies();
|
||||
|
||||
services.AddMediatR(typeof(BlogCreatedEventHandler).GetTypeInfo().Assembly);
|
||||
|
||||
ContainerBuilder builder = new ContainerBuilder();
|
||||
builder.Populate(services);
|
||||
builder.RegisterModule(new ConfigurationModule(Configuration));
|
||||
|
||||
serviceProvider = new AutofacServiceProvider(builder.Build());
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
|
||||
private void LoadInfrastructureAssemblies()
|
||||
{
|
||||
string[] fileNames = Directory.EnumerateFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll", SearchOption.TopDirectoryOnly)
|
||||
.Where(filePath => Path.GetFileName(filePath).StartsWith("Jambo.Consumer.Infrastructure", StringComparison.OrdinalIgnoreCase))
|
||||
.ToArray();
|
||||
|
||||
foreach (string file in fileNames)
|
||||
AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
IMediator mediator = serviceProvider.GetService<IMediator>();
|
||||
ISubscriber subscriber = serviceProvider.GetService<ISubscriber>();
|
||||
|
||||
subscriber.Listen(mediator);
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.WriteLine($"{DateTime.Now.ToString()} Waiting for events..");
|
||||
Thread.Sleep(1000 * 60);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
source/Jambo.Consumer.UI/appsettings.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
19
source/Jambo.Consumer.UI/autofac.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"defaultAssembly": "Jambo.Consumer.Infrastructure",
|
||||
"modules": [
|
||||
{
|
||||
"type": "Jambo.Consumer.Infrastructure.Modules.ApplicationModule",
|
||||
"properties": {
|
||||
"ConnectionString": "mongodb://10.0.75.1:27017",
|
||||
"DatabaseName": "jambov32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Jambo.Consumer.Infrastructure.Modules.BusModule",
|
||||
"properties": {
|
||||
"BrokerList": "10.0.75.1:9092",
|
||||
"Topic": "jambov32"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
49
source/Jambo.Consumer.sln
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Domain", "Jambo.Domain\Jambo.Domain.csproj", "{ED9F252F-10E5-4A65-ADA0-122D61D655A4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Consumer.Application", "Jambo.Consumer.Application\Jambo.Consumer.Application.csproj", "{601F90B1-4BE4-462E-8595-AAAA49B80405}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Domain.UnitTests", "Jambo.Domain.UnitTests\Jambo.Domain.UnitTests.csproj", "{B59AD81D-93F9-425F-8F87-DF13561BE424}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Consumer.Infrastructure", "Jambo.Consumer.Infrastructure\Jambo.Consumer.Infrastructure.csproj", "{645C9138-DA59-48C8-A15E-D720874DD148}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jambo.Consumer.UI", "Jambo.Consumer.UI\Jambo.Consumer.UI.csproj", "{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{ED9F252F-10E5-4A65-ADA0-122D61D655A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ED9F252F-10E5-4A65-ADA0-122D61D655A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ED9F252F-10E5-4A65-ADA0-122D61D655A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ED9F252F-10E5-4A65-ADA0-122D61D655A4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B59AD81D-93F9-425F-8F87-DF13561BE424}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B59AD81D-93F9-425F-8F87-DF13561BE424}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B59AD81D-93F9-425F-8F87-DF13561BE424}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B59AD81D-93F9-425F-8F87-DF13561BE424}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{645C9138-DA59-48C8-A15E-D720874DD148}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{645C9138-DA59-48C8-A15E-D720874DD148}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{645C9138-DA59-48C8-A15E-D720874DD148}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{645C9138-DA59-48C8-A15E-D720874DD148}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {54AE4BA0-6081-4892-A9B9-3F8D15D994C8}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,10 +1,10 @@
|
||||
namespace Jambo.Domain.Exceptions
|
||||
{
|
||||
public class BlogDomainException : JamboException
|
||||
public class DomainException : JamboException
|
||||
{
|
||||
public string BusinessMessage { get; set; }
|
||||
|
||||
public BlogDomainException(string businessMessage)
|
||||
public DomainException(string businessMessage)
|
||||
{
|
||||
BusinessMessage = businessMessage;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace Jambo.Domain.Model.Blogs
|
||||
{
|
||||
if (enabled == false)
|
||||
{
|
||||
throw new BlogDomainException("The blog is disabled. Enable this before making any changes.");
|
||||
throw new DomainException("The blog is disabled. Enable this before making any changes.");
|
||||
}
|
||||
|
||||
Raise(BlogUrlUpdatedDomainEvent.Create(this, url));
|
||||
@@ -43,7 +43,7 @@ namespace Jambo.Domain.Model.Blogs
|
||||
{
|
||||
if (enabled == true)
|
||||
{
|
||||
throw new BlogDomainException("The blog is already enabled.");
|
||||
throw new DomainException("The blog is already enabled.");
|
||||
}
|
||||
|
||||
Raise(BlogEnabledDomainEvent.Create(this));
|
||||
@@ -53,7 +53,7 @@ namespace Jambo.Domain.Model.Blogs
|
||||
{
|
||||
if (enabled == false)
|
||||
{
|
||||
throw new BlogDomainException("The blog is already disabled.");
|
||||
throw new DomainException("The blog is already disabled.");
|
||||
}
|
||||
|
||||
Raise(BlogDisabledDomainEvent.Create(this));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Jambo.Domain.Model.Blogs.Events
|
||||
namespace Jambo.Domain.Model.Blogs.Events
|
||||
{
|
||||
using System;
|
||||
|
||||
public class BlogEnabledDomainEvent : DomainEvent
|
||||
{
|
||||
public BlogEnabledDomainEvent(Guid aggregateRootId, int version,
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Jambo.Domain.Model.Blogs
|
||||
public Url(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new BlogDomainException("The url field is required");
|
||||
throw new DomainException("The url field is required");
|
||||
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Jambo.Domain.Model.Posts
|
||||
public Content(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new BlogDomainException("The content field is required");
|
||||
throw new DomainException("The content field is required");
|
||||
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
@@ -28,3 +28,6 @@ namespace Jambo.Domain.Model.Posts.Events
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,29 +2,29 @@
|
||||
|
||||
namespace Jambo.Domain.Model.Posts.Events
|
||||
{
|
||||
public class PostCreatedDomainEvent : DomainEvent
|
||||
public class PostCreatedDomainEvent : DomainEvent
|
||||
{
|
||||
public Guid BlogId { get; private set; }
|
||||
public int BlogVersion { get; private set; }
|
||||
|
||||
public PostCreatedDomainEvent(Guid aggregateRootId, int version,
|
||||
DateTime createdDate, Header header, Guid blogId, int blogVersion)
|
||||
: base(aggregateRootId, version, createdDate, header)
|
||||
{
|
||||
public Guid BlogId { get; private set; }
|
||||
public int BlogVersion { get; private set; }
|
||||
BlogId = blogId;
|
||||
BlogVersion = blogVersion;
|
||||
}
|
||||
|
||||
public PostCreatedDomainEvent(Guid aggregateRootId, int version,
|
||||
DateTime createdDate, Header header, Guid blogId, int blogVersion)
|
||||
: base(aggregateRootId, version, createdDate, header)
|
||||
{
|
||||
BlogId = blogId;
|
||||
BlogVersion = blogVersion;
|
||||
}
|
||||
public static PostCreatedDomainEvent Create(AggregateRoot aggregateRoot,
|
||||
Guid blogId, int blogVersion)
|
||||
{
|
||||
if (aggregateRoot == null)
|
||||
throw new ArgumentNullException("aggregateRoot");
|
||||
|
||||
public static PostCreatedDomainEvent Create(AggregateRoot aggregateRoot,
|
||||
Guid blogId, int blogVersion)
|
||||
{
|
||||
if (aggregateRoot == null)
|
||||
throw new ArgumentNullException("aggregateRoot");
|
||||
PostCreatedDomainEvent domainEvent = new PostCreatedDomainEvent(
|
||||
aggregateRoot.Id, aggregateRoot.Version, DateTime.UtcNow, null, blogId, blogVersion);
|
||||
|
||||
PostCreatedDomainEvent domainEvent = new PostCreatedDomainEvent(
|
||||
aggregateRoot.Id, aggregateRoot.Version, DateTime.UtcNow, null, blogId, blogVersion);
|
||||
|
||||
return domainEvent;
|
||||
}
|
||||
return domainEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Jambo.Domain.Model.Posts
|
||||
{
|
||||
if (enabled == false)
|
||||
{
|
||||
throw new BlogDomainException("The post is already disabled.");
|
||||
throw new DomainException("The post is already disabled.");
|
||||
}
|
||||
|
||||
Raise(PostDisabledDomainEvent.Create(this));
|
||||
@@ -57,12 +57,12 @@ namespace Jambo.Domain.Model.Posts
|
||||
{
|
||||
if (enabled == false)
|
||||
{
|
||||
throw new BlogDomainException("The post is disabled. Enable this before making any changes.");
|
||||
throw new DomainException("The post is disabled. Enable this before making any changes.");
|
||||
}
|
||||
|
||||
if (published == false)
|
||||
{
|
||||
throw new BlogDomainException("The post is already hidden.");
|
||||
throw new DomainException("The post is already hidden.");
|
||||
}
|
||||
|
||||
Raise(PostHiddenDomainEvent.Create(this));
|
||||
@@ -72,7 +72,7 @@ namespace Jambo.Domain.Model.Posts
|
||||
{
|
||||
if (enabled == true)
|
||||
{
|
||||
throw new BlogDomainException("The post is already enabled.");
|
||||
throw new DomainException("The post is already enabled.");
|
||||
}
|
||||
|
||||
Raise(PostEnabledDomainEvent.Create(this));
|
||||
@@ -82,7 +82,7 @@ namespace Jambo.Domain.Model.Posts
|
||||
{
|
||||
if (enabled == false)
|
||||
{
|
||||
throw new BlogDomainException("The blog is disabled. Enable this before making any changes.");
|
||||
throw new DomainException("The blog is disabled. Enable this before making any changes.");
|
||||
}
|
||||
|
||||
Raise(PostContentUpdatedDomainEvent.Create(this, title, content));
|
||||
@@ -92,12 +92,12 @@ namespace Jambo.Domain.Model.Posts
|
||||
{
|
||||
if (enabled == false)
|
||||
{
|
||||
throw new BlogDomainException("The blog is disabled. Enable this before making any changes.");
|
||||
throw new DomainException("The blog is disabled. Enable this before making any changes.");
|
||||
}
|
||||
|
||||
if (published == true)
|
||||
{
|
||||
throw new BlogDomainException("The post is already published.");
|
||||
throw new DomainException("The post is already published.");
|
||||
}
|
||||
|
||||
Raise(PostPublishedDomainEvent.Create(this));
|
||||
@@ -107,12 +107,12 @@ namespace Jambo.Domain.Model.Posts
|
||||
{
|
||||
if (enabled == false)
|
||||
{
|
||||
throw new BlogDomainException("The blog is disabled. Enable this before making any changes.");
|
||||
throw new DomainException("The blog is disabled. Enable this before making any changes.");
|
||||
}
|
||||
|
||||
if (published == true)
|
||||
{
|
||||
throw new BlogDomainException("The post is already hidden.");
|
||||
throw new DomainException("The post is already hidden.");
|
||||
}
|
||||
|
||||
Raise(CommentCreatedDomainEvent.Create(this, comment.Id, comment.Message));
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Jambo.Domain.Model.Posts
|
||||
public Title(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new BlogDomainException("The title field is required");
|
||||
throw new DomainException("The title field is required");
|
||||
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.ServiceBus
|
||||
namespace Jambo.Domain.ServiceBus
|
||||
{
|
||||
public interface IPublisher : IDisposable
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using MediatR;
|
||||
using System;
|
||||
|
||||
namespace Jambo.ServiceBus
|
||||
namespace Jambo.Domain.ServiceBus
|
||||
{
|
||||
public interface ISubscriber : IDisposable
|
||||
{
|
||||
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -4,28 +4,28 @@ using Jambo.Domain.Model.Blogs;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Blogs
|
||||
{
|
||||
public class CreatePostCommandHandler : IAsyncRequestHandler<CreateBlogCommand, Guid>
|
||||
public class CreatePostCommandHandler : IAsyncRequestHandler<CreateBlogCommand, Guid>
|
||||
{
|
||||
private readonly IPublisher bus;
|
||||
|
||||
public CreatePostCommandHandler(IPublisher bus)
|
||||
{
|
||||
private readonly IPublisher bus;
|
||||
this.bus = bus ?? throw new ArgumentNullException(nameof(bus));
|
||||
}
|
||||
|
||||
public CreatePostCommandHandler(IPublisher bus)
|
||||
{
|
||||
this.bus = bus ?? throw new ArgumentNullException(nameof(bus));
|
||||
}
|
||||
public async Task<Guid> Handle(CreateBlogCommand command)
|
||||
{
|
||||
Blog blog = Blog.Create();
|
||||
blog.Start();
|
||||
blog.UpdateUrl(Url.Create(command.Url));
|
||||
|
||||
public async Task<Guid> Handle(CreateBlogCommand command)
|
||||
{
|
||||
Blog blog = Blog.Create();
|
||||
blog.Start();
|
||||
blog.UpdateUrl(Url.Create(command.Url));
|
||||
await bus.Publish(blog.GetEvents(), command.Header);
|
||||
|
||||
await bus.Publish(blog.GetEvents(), command.Header);
|
||||
|
||||
return blog.Id;
|
||||
}
|
||||
return blog.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Jambo.Domain.Model.Blogs;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Blogs
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.Producer.Application.Commands.Blogs;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Blogs
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.Producer.Application.Commands.Blogs;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Blogs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Jambo.Producer.Application.Commands.Posts;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -3,7 +3,7 @@ using Jambo.Producer.Application.Commands.Blogs;
|
||||
using Jambo.Producer.Application.Commands.Posts;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -3,10 +3,10 @@ using Jambo.Producer.Application.Commands.Blogs;
|
||||
using Jambo.Producer.Application.Commands.Posts;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Posts
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Jambo.Producer.Application.Commands.Posts;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Posts
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Jambo.Producer.Application.Commands.Posts;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Posts
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@ using Jambo.Producer.Application.Commands.Blogs;
|
||||
using Jambo.Producer.Application.Commands.Posts;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Posts
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@ using Jambo.Producer.Application.Commands.Blogs;
|
||||
using Jambo.Producer.Application.Commands.Posts;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Posts
|
||||
{
|
||||
@@ -34,3 +34,4 @@ namespace Jambo.Producer.Application.CommandHandlers.Posts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Blogs
|
||||
{
|
||||
[DataContract]
|
||||
public class CreateBlogCommand : CommandBase, IRequest<Guid>
|
||||
{
|
||||
[StringLength(100, MinimumLength = 10)]
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Url { get; private set; }
|
||||
|
||||
|
||||
@@ -4,14 +4,12 @@ using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Blogs
|
||||
{
|
||||
[DataContract]
|
||||
public class DisableBlogCommand : CommandBase, IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
|
||||
@@ -4,14 +4,12 @@ using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Blogs
|
||||
{
|
||||
[DataContract]
|
||||
public class EnableBlogCommand : CommandBase, IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Blogs
|
||||
@@ -8,12 +7,9 @@ namespace Jambo.Producer.Application.Commands.Blogs
|
||||
[DataContract]
|
||||
public class UpdateBlogUrlCommand : CommandBase, IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
[StringLength(100, MinimumLength = 10)]
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Url { get; private set; }
|
||||
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
using MediatR;
|
||||
using System.Runtime.Serialization;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Posts
|
||||
{
|
||||
[DataContract]
|
||||
public class CreateCommentCommand : CommandBase, IRequest<Guid>
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid PostId { get; private set; }
|
||||
|
||||
[StringLength(100, MinimumLength = 10)]
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Comment { get; private set; }
|
||||
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
using MediatR;
|
||||
using System.Runtime.Serialization;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Posts
|
||||
{
|
||||
[DataContract]
|
||||
public class CreatePostCommand : CommandBase, IRequest<Guid>
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid BlogId { get; private set; }
|
||||
|
||||
[StringLength(100, MinimumLength = 10)]
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Title { get; private set; }
|
||||
|
||||
[StringLength(100, MinimumLength = 10)]
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Content { get; private set; }
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Posts
|
||||
{
|
||||
[DataContract]
|
||||
public class DisablePostCommand : CommandBase, IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Posts
|
||||
{
|
||||
[DataContract]
|
||||
public class EnablePostCommand : CommandBase, IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Posts
|
||||
{
|
||||
[DataContract]
|
||||
public class HidePostCommand : CommandBase, IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Posts
|
||||
{
|
||||
[DataContract]
|
||||
public class PublishPostCommand : CommandBase, IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
|
||||
@@ -2,24 +2,18 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jambo.Producer.Application.Commands.Posts
|
||||
{
|
||||
[DataContract]
|
||||
public class UpdatePostContentCommand : CommandBase, IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
[StringLength(100, MinimumLength = 10)]
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Title { get; private set; }
|
||||
|
||||
[StringLength(100, MinimumLength = 10)]
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Content { get; private set; }
|
||||
|
||||
|
||||
@@ -6,14 +6,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" />
|
||||
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Producer.Application.Queries
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Producer.Application.Queries
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
namespace Jambo.Producer.Infrastructure.DataAccess
|
||||
{
|
||||
using Jambo.Domain.Model;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
public class MongoContext
|
||||
{
|
||||
private readonly MongoClient mongoClient;
|
||||
private readonly IMongoDatabase database;
|
||||
|
||||
public MongoContext(string connectionString, string databaseName)
|
||||
{
|
||||
this.mongoClient = new MongoClient(connectionString);
|
||||
this.database = mongoClient.GetDatabase(databaseName);
|
||||
Map();
|
||||
}
|
||||
|
||||
public void DatabaseReset(string databaseName)
|
||||
{
|
||||
mongoClient.DropDatabase(databaseName);
|
||||
}
|
||||
|
||||
public IMongoCollection<Blog> Blogs
|
||||
{
|
||||
get
|
||||
{
|
||||
return database.GetCollection<Blog>("Blogs");
|
||||
}
|
||||
}
|
||||
|
||||
public IMongoCollection<Post> Posts
|
||||
{
|
||||
get
|
||||
{
|
||||
return database.GetCollection<Post>("Posts");
|
||||
}
|
||||
}
|
||||
|
||||
private void Map()
|
||||
{
|
||||
BsonClassMap.RegisterClassMap<Entity>(cm =>
|
||||
{
|
||||
cm.MapIdProperty(c => c.Id);
|
||||
});
|
||||
|
||||
BsonClassMap.RegisterClassMap<AggregateRoot>(cm =>
|
||||
{
|
||||
cm.MapProperty(c => c.Version).SetElementName("_version");
|
||||
});
|
||||
|
||||
BsonClassMap.RegisterClassMap<Blog>(cm =>
|
||||
{
|
||||
cm.MapField("url").SetElementName("url");
|
||||
cm.MapField("rating").SetElementName("rating");
|
||||
cm.MapField("enabled").SetElementName("enabled");
|
||||
});
|
||||
|
||||
BsonClassMap.RegisterClassMap<Post>(cm =>
|
||||
{
|
||||
cm.MapField("title").SetElementName("title");
|
||||
cm.MapField("content").SetElementName("content");
|
||||
cm.MapField("blogId").SetElementName("blogId");
|
||||
cm.MapField("enabled").SetElementName("enabled");
|
||||
cm.MapField("published").SetElementName("published");
|
||||
});
|
||||
|
||||
BsonClassMap.RegisterClassMap<Comment>(cm =>
|
||||
{
|
||||
cm.MapField("message").SetElementName("message");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||