├── src ├── Architecture │ ├── ThreeTier │ │ ├── Aoxe.ThreeTier.Abstractions │ │ │ ├── GlobalUsings.cs │ │ │ ├── DataAccess │ │ │ │ └── DataAccessLayer.cs │ │ │ ├── BusinessLogic │ │ │ │ ├── BusinessLogicLayer.cs │ │ │ │ └── MessageHandler.cs │ │ │ └── Aoxe.ThreeTier.Abstractions.csproj │ │ └── Aoxe.ThreeTier │ │ │ ├── GlobalUsings.cs │ │ │ ├── Aoxe.IServiceCollection.Extensions.cs │ │ │ ├── Aoxe.IServiceCollection.Extensions.Dal.cs │ │ │ ├── Aoxe.IServiceCollection.Extensions.Bll.cs │ │ │ └── Aoxe.ThreeTier.csproj │ └── DDD │ │ ├── Aoxe.DDD.Abstractions │ │ ├── Application │ │ │ ├── IntegrationEvent.cs │ │ │ ├── ApplicationService.cs │ │ │ └── IntegrationEventHandler.cs │ │ ├── Domain │ │ │ ├── Command.cs │ │ │ ├── ValueObject.cs │ │ │ ├── DomainService.cs │ │ │ ├── Factory.cs │ │ │ ├── AggregateRoot.cs │ │ │ ├── DomainEventHandler.cs │ │ │ ├── DomainEvent.cs │ │ │ └── Entity.cs │ │ ├── GlobalUsings.cs │ │ ├── Infrastructure │ │ │ ├── Messaging │ │ │ │ └── IMessageBus.cs │ │ │ └── Repository │ │ │ │ └── Repository.cs │ │ └── Aoxe.DDD.Abstractions.csproj │ │ └── Aoxe.DDD │ │ ├── DomainEventSubscriber.cs │ │ ├── Aoxe.IServiceCollection.Extensions.Repository.cs │ │ ├── Aoxe.IServiceCollection.Extensions.cs │ │ ├── Aoxe.IServiceCollection.Extensions.Application.cs │ │ ├── GlobalUsings.cs │ │ ├── Aoxe.IServiceCollection.Extensions.Domain.cs │ │ ├── Aoxe.DDD.csproj │ │ └── DomainEventPublisher.cs ├── Client │ ├── Http │ │ ├── Formatters │ │ │ ├── Aoxe.Client.Http.MsgPack │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── AoxeClientFormatterOptionsExtensions.cs │ │ │ │ └── Aoxe.Client.Http.MsgPack.csproj │ │ │ ├── Aoxe.Client.Http.Protobuf │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── AoxeClientFormatterOptionsExtensions.cs │ │ │ │ └── Aoxe.Client.Http.Protobuf.csproj │ │ │ ├── Aoxe.Client.Http.Zeroformatter │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── AoxeClientFormatterOptionsExtensions.cs │ │ │ │ └── Aoxe.Client.Http.Zeroformatter.csproj │ │ │ ├── Aoxe.Client.Http.Jil │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── AoxeClientFormatterOptionsExtensions.cs │ │ │ │ └── Aoxe.Client.Http.Jil.csproj │ │ │ ├── Aoxe.Client.Http.Utf8Json │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── AoxeClientFormatterOptionsExtensions.cs │ │ │ │ └── Aoxe.Client.Http.Utf8Json.csproj │ │ │ └── Aoxe.Client.Http.Formatter │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── AoxeClientFormatterOptions.cs │ │ │ │ ├── AoxeHttpClientFormatterFactory.cs │ │ │ │ ├── AoxeHttpClientFormatter.cs │ │ │ │ ├── AoxeHttpClientStreamFormatter.cs │ │ │ │ ├── Aoxe.Client.Http.Formatter.csproj │ │ │ │ └── AoxeHttpClientTextFormatter.cs │ │ └── Aoxe.Client.Http │ │ │ ├── Internal │ │ │ ├── StringExtensions.cs │ │ │ ├── TaskExtensions.cs │ │ │ ├── AsyncExtensions.cs │ │ │ ├── TaskCompletionSource.cs │ │ │ └── ZaabyClientProxy.cs │ │ │ ├── GlobalUsings.cs │ │ │ └── Aoxe.Client.Http.csproj │ └── Grpc │ │ └── Aoxe.Client.Grpc │ │ └── Aoxe.Client.Grpc.csproj ├── Aoxe.Shared │ ├── LoadMode.cs │ ├── GlobalUsings.cs │ ├── TypePair.cs │ ├── Aoxe.IServiceCollection.Extensions.LoadByDirectories.cs │ ├── LoadHelper.LoadByAssemblies.cs │ ├── Aoxe.IServiceCollection.Extensions.LoadByAssemblies.cs │ ├── LoadHelper.LoadTypes.cs │ ├── Aoxe.IServiceCollection.Extensions.Register.cs │ ├── LoadHelper.LoadByDirectory.cs │ └── Aoxe.Shared.csproj ├── Server │ ├── WebApi │ │ ├── Aoxe.WebApi.Formatters │ │ │ ├── Aoxe.AspNetCore.Formatters │ │ │ │ ├── SerializeProcessor.cs │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── AoxeOutputFormatter.cs │ │ │ │ ├── StreamExtensions.cs │ │ │ │ ├── AoxeTextOutputFormatter.cs │ │ │ │ ├── AoxeInputFormatter.cs │ │ │ │ ├── AoxeTextInputFormatter.cs │ │ │ │ └── Aoxe.AspNetCore.Formatters.csproj │ │ │ ├── Aoxe.AspNetCore.Formatters.MsgPack │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── README.md │ │ │ │ ├── MvcBuilderExtension.cs │ │ │ │ ├── MvcOptionsExtension.cs │ │ │ │ └── Aoxe.AspNetCore.Formatters.MsgPack.csproj │ │ │ ├── Aoxe.AspNetCore.Formatters.Protobuf │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── README.md │ │ │ │ ├── MvcBuilderExtension.cs │ │ │ │ ├── MvcOptionsExtension.cs │ │ │ │ └── Aoxe.AspNetCore.Formatters.Protobuf.csproj │ │ │ ├── Aoxe.AspNetCore.Formatters.ZeroFormatter │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── README.md │ │ │ │ ├── MvcBuilderExtension.cs │ │ │ │ ├── MvcOptionsExtension.cs │ │ │ │ └── Aoxe.AspNetCore.Formatters.ZeroFormatter.csproj │ │ │ ├── Aoxe.AspNetCore.Formatters.Jil │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── README.md │ │ │ │ ├── MvcBuilderExtension.cs │ │ │ │ ├── MvcOptionsExtension.cs │ │ │ │ └── Aoxe.AspNetCore.Formatters.Jil.csproj │ │ │ ├── Aoxe.AspNetCore.Formatters.Utf8Json │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── README.md │ │ │ │ ├── MvcBuilderExtension.cs │ │ │ │ ├── MvcOptionsExtension.cs │ │ │ │ └── Aoxe.AspNetCore.Formatters.Utf8Json.csproj │ │ │ └── README.md │ │ └── Aoxe.WebApi │ │ │ ├── Aoxe.IServiceCollection.Extensions.UnitOfWork.cs │ │ │ ├── UnitOfWorkMiddleware.cs │ │ │ ├── AoxeAppServiceControllerFeatureProvider.cs │ │ │ ├── Aoxe.IApplicationBuilder.Extensions.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── AoxeActionModelConvention.cs │ │ │ ├── Aoxe.WebApi.csproj │ │ │ └── ErrorHandlingMiddleware.cs │ └── Grpc │ │ └── Aoxe.Grpc │ │ ├── GlobalUsings.cs │ │ └── Aoxe.Grpc.csproj ├── Aoxe.Core │ ├── AoxeMessage.cs │ ├── AoxeError.cs │ ├── AoxeException.cs │ ├── AoxeUnpublishedMessage.cs │ ├── AoxePublishedMessage.cs │ └── Aoxe.Core.csproj ├── Aoxe │ ├── AoxeHost.Extensions.DDD.cs │ ├── AoxeHost.Extensions.ThreeTies.cs │ ├── AoxeHost.Extensions.UnitOfWork.cs │ ├── AoxeHost.Extensions.Client.cs │ ├── GlobalUsings.cs │ ├── AoxeHost.Extensions.HostService.cs │ └── Aoxe.csproj ├── PubSub │ ├── Aoxe.AutoSubscribe │ │ └── Aoxe.AutoSubscribe.csproj │ ├── Aoxe.PubSub.Local │ │ ├── Aoxe.PubSub.Local.MediatR │ │ │ └── Aoxe.PubSub.Local.MediatR.csproj │ │ ├── Aoxe.PubSub.Local.Abstractions │ │ │ └── Aoxe.PubSub.Local.Abstractions.csproj │ │ └── Aoxe.PubSub.Local.MessagePipe │ │ │ └── Aoxe.PubSub.Local.MessagePipe.csproj │ └── Aoxe.PubSub.Distributed │ │ ├── Aoxe.PubSub.Distributed.Kafka │ │ └── Aoxe.PubSub.Distributed.Kafka.csproj │ │ ├── Aoxe.PubSub.Distributed.NATS │ │ └── Aoxe.PubSub.Distributed.NATS.csproj │ │ ├── Aoxe.PubSub.Distributed.ZeroMQ │ │ └── Aoxe.PubSub.Distributed.ZeroMQ.csproj │ │ ├── Aoxe.PubSub.Distributed.RabbitMQ │ │ └── Aoxe.PubSub.Distributed.RabbitMQ.csproj │ │ ├── Aoxe.PubSub.Distributed.RedisStream │ │ └── Aoxe.PubSub.Distributed.RedisStream.csproj │ │ ├── Aoxe.PubSub.Distributed.Abstractions │ │ └── Aoxe.PubSub.Distributed.Abstractions.csproj │ │ └── Aoxe.PubSub.LocalMessageStorage.EntityFramework │ │ └── Aoxe.PubSub.LocalMessageStorage.EntityFramework.csproj └── Scheduling │ ├── Aoxe.Quartz │ └── Aoxe.Quartz.csproj │ └── Aoxe.Hangfire │ └── Aoxe.Hangfire.csproj ├── demo ├── Aoxe │ ├── Interfaces │ │ ├── IService.cs │ │ ├── IMessage.cs │ │ ├── ServiceAttribute.cs │ │ ├── Interfaces.csproj │ │ └── IConsumer.cs │ ├── AliceSystem │ │ ├── AliceServices │ │ │ ├── GlobalUsings.cs │ │ │ ├── NonContractService.cs │ │ │ ├── RouteAttributeService.cs │ │ │ └── AliceServices.csproj │ │ └── IAliceServices │ │ │ ├── Apple.cs │ │ │ ├── IAliceServices.csproj │ │ │ └── IAliceService.cs │ ├── AliceHost │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Controllers │ │ │ └── HealthCheck.cs │ │ ├── Program.cs │ │ ├── GlobalUsings.cs │ │ └── AliceHost.csproj │ ├── CarolHost │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Program.cs │ │ ├── GlobalUsings.cs │ │ └── CarolHost.csproj │ ├── BobSystem │ │ ├── IBobServices │ │ │ ├── IBobServices.csproj │ │ │ └── IBobService.cs │ │ └── BobServices │ │ │ └── BobServices.csproj │ ├── CarolSystem │ │ ├── ICarolServices │ │ │ ├── ICarolServices.csproj │ │ │ └── ICarolService.cs │ │ └── CarolServices │ │ │ ├── CarolServices.csproj │ │ │ └── CarolService.cs │ └── BobHost │ │ ├── GlobalUsings.cs │ │ ├── BobHost.csproj │ │ └── Program.cs ├── Aoxe.AspNetCore.Formatters.Demo │ ├── TestEnum.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── Controllers │ │ └── ValuesController.cs │ ├── Program.cs │ ├── GlobalUsings.cs │ ├── TestDto.cs │ ├── Startup.cs │ └── Aoxe.AspNetCore.Formatters.Demo.csproj ├── Aoxe.DDD │ ├── MicroService │ │ ├── Domain │ │ │ ├── ValueObjects │ │ │ │ ├── Gender.cs │ │ │ │ └── Address.cs │ │ │ ├── IRepositories │ │ │ │ └── IUserRepository.cs │ │ │ ├── Domain.csproj │ │ │ ├── Entities │ │ │ │ └── Card.cs │ │ │ ├── DomainEvents │ │ │ │ ├── UserCreatedEvent.cs │ │ │ │ ├── UserNameChangedEvent.cs │ │ │ │ ├── UserBirthdayCelebratedEvent.cs │ │ │ │ ├── UserTagsSetEvent.cs │ │ │ │ └── UserCardAddedEvent.cs │ │ │ ├── README.md │ │ │ └── DomainServices │ │ │ │ └── UserDomainService.cs │ │ ├── ServiceHost │ │ │ ├── appsettings.Development.json │ │ │ ├── Program.cs │ │ │ ├── appsettings.json │ │ │ ├── UowMiddleware.cs │ │ │ ├── ServiceCollectionExtension.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── ServiceHost.csproj │ │ │ └── DomainEventBackgroundService.cs │ │ ├── Application │ │ │ ├── Application.csproj │ │ │ └── README.md │ │ ├── README.md │ │ └── Repository │ │ │ ├── Repository.csproj │ │ │ ├── CustomDbContext.cs │ │ │ ├── README.md │ │ │ └── UserRepository.cs │ └── BackendForFrontend │ │ ├── BackendForBrowser │ │ ├── images │ │ │ ├── Richardson_tAGP_01.png │ │ │ ├── Richardson_tAGP_03.png │ │ │ ├── Richardson_tAGP_04.png │ │ │ └── Richardson_tAGP_05.png │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── Program.cs │ │ ├── BackendForBrowser.csproj │ │ ├── ServiceCollectionExtension.cs │ │ ├── Startup.cs │ │ └── README.md │ │ ├── QueryService │ │ ├── Models │ │ │ └── UserReadModel.cs │ │ ├── QueryService.csproj │ │ ├── README.md │ │ └── UserQueryService.cs │ │ └── README.md └── Aoxe.ThreeTier │ ├── Model │ ├── Model.csproj │ └── User.cs │ ├── BusinessLogicLayer │ ├── Dtos │ │ ├── LoginRequestParam.cs │ │ └── CreateUserDto.cs │ ├── BusinessLogicLayer.csproj │ └── UserBll.cs │ ├── WebApiHost │ ├── appsettings.Development.json │ ├── Program.cs │ ├── ServiceCollectionExtension.cs │ ├── appsettings.json │ ├── GlobalUsings.cs │ ├── WebApiHost.csproj │ └── Startup.cs │ ├── DataAccessLayer │ ├── DataAccessLayer.csproj │ └── UserDal.cs │ └── ConsoleHost │ ├── ConsoleHost.csproj │ └── Program.cs ├── tests ├── Aoxe.AspNetCore.Formatters.Tests │ ├── GlobalUsing.cs │ ├── MsgPack.Formatter.Test.cs │ ├── Utf8Json.Formatter.Test.cs │ ├── Protobuf.Formatter.Test.cs │ ├── ZeroFormatter.Formatter.Test.cs │ ├── AspNetCoreFormatterTest.cs │ ├── Jil.Formatter.Test.cs │ ├── Aoxe.AspNetCore.Formatters.Tests.csproj │ ├── AspNetCoreFormatterTest.Text.cs │ └── AspNetCoreFormatterTest.Stream.cs └── Aoxe.Shared.Test │ └── Aoxe.Shared.Test.csproj ├── README.md ├── azure-pipelines.yml └── LICENSE /src/Architecture/ThreeTier/Aoxe.ThreeTier.Abstractions/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; -------------------------------------------------------------------------------- /demo/Aoxe/Interfaces/IService.cs: -------------------------------------------------------------------------------- 1 | namespace Interfaces 2 | { 3 | public interface IService 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /demo/Aoxe/Interfaces/IMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Interfaces 2 | { 3 | public interface IMessage 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.MsgPack/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using Aoxe.Client.Http.Formatter; -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Protobuf/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using Aoxe.Client.Http.Formatter; -------------------------------------------------------------------------------- /src/Aoxe.Shared/LoadMode.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Shared; 2 | 3 | public enum LoadTypesMode 4 | { 5 | LoadBySpecify, 6 | LoadByAllDirectory 7 | } 8 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Zeroformatter/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using Aoxe.Client.Http.Formatter; -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Jil/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using Aoxe.Client.Http.Formatter; 4 | global using Jil; 5 | -------------------------------------------------------------------------------- /demo/Aoxe/Interfaces/ServiceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Interfaces 4 | { 5 | public class ServiceAttribute : Attribute 6 | { 7 | 8 | } 9 | } -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Application/IntegrationEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Application; 2 | 3 | public interface IIntegrationEvent { } 4 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Utf8Json/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using Aoxe.Client.Http.Formatter; 4 | global using Utf8Json; 5 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters/SerializeProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters; 2 | 3 | public class SerializeProcessor { } 4 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/TestEnum.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Demo; 2 | 3 | public enum TestEnum 4 | { 5 | Apple, 6 | Banana, 7 | Pear 8 | } 9 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/ValueObjects/Gender.cs: -------------------------------------------------------------------------------- 1 | namespace Domain.ValueObjects 2 | { 3 | public enum Gender 4 | { 5 | Male, 6 | Female 7 | } 8 | } -------------------------------------------------------------------------------- /demo/Aoxe/AliceSystem/AliceServices/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using System; 4 | global using Interfaces; 5 | global using Microsoft.AspNetCore.Mvc; 6 | -------------------------------------------------------------------------------- /demo/Aoxe/Interfaces/Interfaces.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/images/Richardson_tAGP_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AoxeTech/Aoxe/HEAD/demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/images/Richardson_tAGP_01.png -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/images/Richardson_tAGP_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AoxeTech/Aoxe/HEAD/demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/images/Richardson_tAGP_03.png -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/images/Richardson_tAGP_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AoxeTech/Aoxe/HEAD/demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/images/Richardson_tAGP_04.png -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/images/Richardson_tAGP_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AoxeTech/Aoxe/HEAD/demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/images/Richardson_tAGP_05.png -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/Model/Model.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Domain/Command.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Domain; 2 | 3 | public interface ICommand : IValueObject { } 4 | 5 | public abstract record Command : ICommand; 6 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Domain/ValueObject.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Domain; 2 | 3 | public interface IValueObject { } 4 | 5 | public abstract record ValueObject : IValueObject; 6 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Domain/DomainService.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Domain; 2 | 3 | public interface IDomainService { } 4 | 5 | public class DomainServiceAttribute : Attribute { } 6 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Threading.Tasks; 4 | global using Aoxe.DDD.Abstractions.Domain; 5 | -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier.Abstractions/DataAccess/DataAccessLayer.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.ThreeTier.Abstractions.DataAccess; 2 | 3 | public interface IDal { } 4 | 5 | public class DalAttribute : Attribute { } 6 | -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier.Abstractions/BusinessLogic/BusinessLogicLayer.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.ThreeTier.Abstractions.BusinessLogic; 2 | 3 | public interface IBll { } 4 | 5 | public class BllAttribute : Attribute { } 6 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Application/ApplicationService.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Application; 2 | 3 | public interface IApplicationService { } 4 | 5 | public class ApplicationServiceAttribute : Attribute { } 6 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceHost/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/Aoxe/CarolHost/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/BusinessLogicLayer/Dtos/LoginRequestParam.cs: -------------------------------------------------------------------------------- 1 | namespace BusinessLogicLayer.Dtos 2 | { 3 | public class LoginRequestParam 4 | { 5 | public string Name { get; set; } 6 | public string Pwd { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Aoxe.Shared/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.IO; 4 | global using System.Linq; 5 | global using System.Reflection; 6 | global using Microsoft.Extensions.DependencyInjection; 7 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceSystem/IAliceServices/Apple.cs: -------------------------------------------------------------------------------- 1 | namespace IAliceServices 2 | { 3 | public class Apple 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Message { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /demo/Aoxe/Interfaces/IConsumer.cs: -------------------------------------------------------------------------------- 1 | namespace Interfaces 2 | { 3 | public interface IConsumer 4 | { 5 | } 6 | 7 | public interface IConsumer : IConsumer 8 | { 9 | void Consume(TMessage message); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Aoxe.Shared; 2 | global using Aoxe.ThreeTier.Abstractions.BusinessLogic; 3 | global using Aoxe.ThreeTier.Abstractions.DataAccess; 4 | global using Microsoft.Extensions.DependencyInjection; 5 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/WebApiHost/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/ServiceHost/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /demo/Aoxe/CarolHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/Aoxe.Shared/TypePair.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Shared; 2 | 3 | public class TypePair(Type interfaceType, Type implementationType) 4 | { 5 | public Type? InterfaceType { get; } = interfaceType; 6 | public Type ImplementationType { get; } = implementationType; 7 | } 8 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Domain/Factory.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Domain; 2 | 3 | public interface IFactory { } 4 | 5 | public interface IFactory : IFactory 6 | where TEntity : IEntity { } 7 | 8 | public class FactoryAttribute : Attribute { } 9 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/BusinessLogicLayer/Dtos/CreateUserDto.cs: -------------------------------------------------------------------------------- 1 | namespace BusinessLogicLayer.Dtos 2 | { 3 | public class CreateUserDto 4 | { 5 | public string Name { get; set; } 6 | public string Pwd { get; set; } 7 | public string Nickname { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Aoxe.Core/AoxeMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Core; 2 | 3 | public abstract record AoxeMessage 4 | { 5 | public Guid Id { get; protected set; } 6 | public string MessageType { get; protected set; } = string.Empty; 7 | public string Content { get; protected set; } = string.Empty; 8 | } 9 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Infrastructure/Messaging/IMessageBus.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Infrastructure.Messaging; 2 | 3 | public interface IMessageBus 4 | { 5 | void Publish(string topic, T message); 6 | ValueTask PublishAsync(string topic, T message); 7 | } 8 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD/DomainEventSubscriber.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD; 2 | 3 | public class DomainEventSubscriber : BackgroundService 4 | { 5 | protected override Task ExecuteAsync(CancellationToken stoppingToken) 6 | { 7 | throw new NotImplementedException(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.MsgPack/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using Microsoft.AspNetCore.Mvc; 3 | global using Microsoft.Extensions.DependencyInjection; 4 | global using Microsoft.Extensions.Primitives; 5 | global using Microsoft.Net.Http.Headers; 6 | -------------------------------------------------------------------------------- /src/Aoxe/AoxeHost.Extensions.DDD.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe; 2 | 3 | public static partial class AoxeHostExtensions 4 | { 5 | public static AoxeHost AddDDD(this AoxeHost aoxeHost) 6 | { 7 | aoxeHost.ConfigureServices(services => services.AddDDD()); 8 | return aoxeHost; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Formatter/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Net.Http; 3 | global using System.Net.Http.Headers; 4 | global using System.Text; 5 | global using System.Threading.Tasks; 6 | global using Aoxe.Core; 7 | global using Aoxe.Serializer.Abstractions; 8 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Protobuf/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using Microsoft.AspNetCore.Mvc; 3 | global using Microsoft.Extensions.DependencyInjection; 4 | global using Microsoft.Extensions.Primitives; 5 | global using Microsoft.Net.Http.Headers; 6 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.ZeroFormatter/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using Microsoft.AspNetCore.Mvc; 3 | global using Microsoft.Extensions.DependencyInjection; 4 | global using Microsoft.Extensions.Primitives; 5 | global using Microsoft.Net.Http.Headers; 6 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/QueryService/Models/UserReadModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace QueryService.Models 4 | { 5 | public class UserReadModel 6 | { 7 | public Guid Id { get; set; } 8 | public string Name { get; set; } 9 | public int Age { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Jil/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using Jil; 3 | global using Microsoft.AspNetCore.Mvc; 4 | global using Microsoft.Extensions.DependencyInjection; 5 | global using Microsoft.Extensions.Primitives; 6 | global using Microsoft.Net.Http.Headers; 7 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceSystem/AliceServices/NonContractService.cs: -------------------------------------------------------------------------------- 1 | namespace AliceServices 2 | { 3 | public class NonContractService : IService 4 | { 5 | public string NonInterfaceTest() 6 | { 7 | return $"This has not implemented any interface.[{DateTime.UtcNow}]"; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Aoxe/AoxeHost.Extensions.ThreeTies.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe; 2 | 3 | public static partial class AoxeHostExtensions 4 | { 5 | public static AoxeHost AddThreeTiers(this AoxeHost aoxeHost) 6 | { 7 | aoxeHost.ConfigureServices(services => services.AddThreeTier()); 8 | return aoxeHost; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Server/Grpc/Aoxe.Grpc/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using System.Collections.Concurrent; 4 | global using System.Reflection; 5 | global using System.Reflection.Emit; 6 | global using System.ServiceModel; 7 | global using Microsoft.Extensions.DependencyInjection; 8 | global using ProtoBuf.Grpc.Server; 9 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/Controllers/ValuesController.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Demo.Controllers; 2 | 3 | [Route("api/[controller]/[action]")] 4 | public class ValuesController : ControllerBase 5 | { 6 | [HttpPost] 7 | public IEnumerable Post([FromBody] IEnumerable dtos) => dtos; 8 | } 9 | -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier/Aoxe.IServiceCollection.Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.ThreeTier; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddThreeTier(this IServiceCollection services) => 6 | services.AddBll().AddMessageHandler().AddDal(); 7 | } 8 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Utf8Json/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using Microsoft.AspNetCore.Mvc; 3 | global using Microsoft.Extensions.DependencyInjection; 4 | global using Microsoft.Extensions.Primitives; 5 | global using Microsoft.Net.Http.Headers; 6 | global using Utf8Json; 7 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Application/Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.IO; 3 | global using System.Text; 4 | global using System.Threading; 5 | global using Aoxe.Serializer.Abstractions; 6 | global using Microsoft.AspNetCore.Mvc.Formatters; 7 | global using Microsoft.Net.Http.Headers; 8 | -------------------------------------------------------------------------------- /demo/Aoxe/BobSystem/IBobServices/IBobServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/QueryService/QueryService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceSystem/IAliceServices/IAliceServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/Aoxe/CarolSystem/ICarolServices/ICarolServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/ServiceHost/Program.cs: -------------------------------------------------------------------------------- 1 | namespace ServiceHost; 2 | 3 | public class Program 4 | { 5 | public static void Main(string[] args) 6 | { 7 | Host.CreateDefaultBuilder(args) 8 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }) 9 | .Build().Run(); 10 | } 11 | } -------------------------------------------------------------------------------- /demo/Aoxe/AliceSystem/AliceServices/RouteAttributeService.cs: -------------------------------------------------------------------------------- 1 | namespace AliceServices; 2 | 3 | [Route("RouteTest/[action]")] 4 | [ServiceAttribute] 5 | public class RouteAttributeService 6 | { 7 | public string RouteAttributeTest() 8 | { 9 | return $"This has not implemented any interface.[{DateTime.UtcNow}]"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /demo/Aoxe/CarolSystem/ICarolServices/ICarolService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Interfaces; 3 | 4 | namespace ICarolServices 5 | { 6 | public interface ICarolService : IService 7 | { 8 | string Hello(); 9 | string SayHelloToAlice(); 10 | string SayHelloToCarol(); 11 | Exception ThrowException(); 12 | } 13 | } -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/Model/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Model 4 | { 5 | public class User 6 | { 7 | public Guid Id { get; set; } 8 | public string Name { get; set; } 9 | public string Pwd { get; set; } 10 | public string Nickname { get; set; } 11 | public DateTime CreatedUtcTime { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/PubSub/Aoxe.AutoSubscribe/Aoxe.AutoSubscribe.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/ServiceHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "PgSqlPrimary": "Host=192.168.78.140;Username=postgres;Password=postgres;Database=postgres" 11 | } 12 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "PgSqlStandby": "Host=192.168.78.141;Username=postgres;Password=postgres;Database=postgres" 11 | } 12 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceHost/Controllers/HealthCheck.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace AliceHost.Controllers 4 | { 5 | [Route("[Controller]")] 6 | public class HealthCheckController : Controller 7 | { 8 | [HttpGet("")] 9 | [HttpHead("")] 10 | public IActionResult Get() 11 | { 12 | return Ok(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/WebApiHost/Program.cs: -------------------------------------------------------------------------------- 1 | namespace WebApiHost; 2 | 3 | public class Program 4 | { 5 | public static void Main(string[] args) => 6 | Host.CreateDefaultBuilder(args) 7 | .ConfigureWebHostDefaults(webBuilder => 8 | { 9 | webBuilder.UseStartup(); 10 | }) 11 | .Build() 12 | .Run(); 13 | } -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Local/Aoxe.PubSub.Local.MediatR/Aoxe.PubSub.Local.MediatR.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/README.md: -------------------------------------------------------------------------------- 1 | # 微服务分层 2 | 3 | --- 4 | 5 | * 领域层(Domain)不依赖其它任何层,需要的功能直接在自己内部通过interface定义。 6 | * 应用层(Application)依赖领域层,并负责工作单元(UOW)的提交。 7 | * 仓储层(Repository)依赖领域层,并实现领域层中的仓储接口。 8 | * Host依赖应用层和仓储层,并负责IOC的注册: 9 | * 领域层:领域服务、领域事件订阅者。 10 | * 应用层:应用服务、应用事件订阅者。 11 | * 仓储层:将仓储实现注册为领域层中的仓储interface,并依据技术选型注册db connection。 12 | * 基础设施:将实现注册为interface,要依据实现的需要选择对应的生命周期。 13 | 14 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/WebApiHost/ServiceCollectionExtension.cs: -------------------------------------------------------------------------------- 1 | namespace WebApiHost; 2 | 3 | public static class ServiceCollectionExtension 4 | { 5 | public static IServiceCollection AddDbConnection(this IServiceCollection services, string connectionString) 6 | { 7 | services.AddScoped(_ => new NpgsqlConnection(connectionString)); 8 | return services; 9 | } 10 | } -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/WebApiHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "ConnectionStrings": { 11 | "PgSql": "Host=192.168.78.140;Username=postgres;Password=postgres;Database=ThreeTier" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Domain/AggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Domain; 2 | 3 | public interface IAggregateRoot : IEntity { } 4 | 5 | public interface IAggregateRoot : IEntity, IAggregateRoot { } 6 | 7 | public abstract class AggregateRoot : Entity, IAggregateRoot { } 8 | 9 | public abstract class AggregateRoot : Entity, IAggregateRoot { } 10 | -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Local/Aoxe.PubSub.Local.Abstractions/Aoxe.PubSub.Local.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Local/Aoxe.PubSub.Local.MessagePipe/Aoxe.PubSub.Local.MessagePipe.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/QueryService/README.md: -------------------------------------------------------------------------------- 1 | # QueryService 2 | 3 | --- 4 | 5 | ## 说明 6 | 7 | * 在微服务级别的CQS中Q端的实现。 8 | * 当前QueryService为一个类库,实际上依据团队规模和开发成本,QueryService可以作为一个单独的微服务而部署。 9 | * CQS中,Q端并不属于OO的范畴,因此使用恰当(技术、成本、资源)的实现即可。 10 | * CQRS中,Q端的读模型实际上也相当于聚合的一种,其提供的接口也可使用**聚合即资源**的restful风格。 11 | * 当前示例使用了ADO.Net,实际操作中可使用ef的dbfirst模式(ef core已取消)、micro orm(如dapper、freesql等)或者一些lambda to sql的封装来实现。 -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Distributed/Aoxe.PubSub.Distributed.Kafka/Aoxe.PubSub.Distributed.Kafka.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Distributed/Aoxe.PubSub.Distributed.NATS/Aoxe.PubSub.Distributed.NATS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Distributed/Aoxe.PubSub.Distributed.ZeroMQ/Aoxe.PubSub.Distributed.ZeroMQ.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Distributed/Aoxe.PubSub.Distributed.RabbitMQ/Aoxe.PubSub.Distributed.RabbitMQ.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Scheduling/Aoxe.Quartz/Aoxe.Quartz.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier/Aoxe.IServiceCollection.Extensions.Dal.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.ThreeTier; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddDal(this IServiceCollection services) => 6 | services 7 | .Register(ServiceLifetime.Scoped) 8 | .Register(ServiceLifetime.Scoped); 9 | } 10 | -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Distributed/Aoxe.PubSub.Distributed.RedisStream/Aoxe.PubSub.Distributed.RedisStream.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Distributed/Aoxe.PubSub.Distributed.Abstractions/Aoxe.PubSub.Distributed.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Demo; 2 | 3 | public class Program 4 | { 5 | public static void Main(string[] args) => 6 | Host.CreateDefaultBuilder(args) 7 | .ConfigureWebHostDefaults(webBuilder => 8 | { 9 | webBuilder.UseStartup(); 10 | }) 11 | .Build() 12 | .Run(); 13 | } 14 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD/Aoxe.IServiceCollection.Extensions.Repository.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddRepository(this IServiceCollection services) => 6 | services 7 | .Register(ServiceLifetime.Scoped) 8 | .Register(ServiceLifetime.Scoped); 9 | } 10 | -------------------------------------------------------------------------------- /src/Server/Grpc/Aoxe.Grpc/Aoxe.Grpc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Aoxe.Core/AoxeError.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Core; 2 | 3 | public class AoxeError 4 | { 5 | public Guid Id { get; set; } 6 | public string Code { get; set; } = string.Empty; 7 | public DateTime ThrowTime { get; set; } = DateTime.UtcNow; 8 | public string Message { get; set; } = string.Empty; 9 | public string Source { get; set; } = string.Empty; 10 | public string StackTrace { get; set; } = string.Empty; 11 | } 12 | -------------------------------------------------------------------------------- /src/Aoxe.Shared/Aoxe.IServiceCollection.Extensions.LoadByDirectories.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Shared; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection FromDirectoriesOf( 6 | this IServiceCollection services, 7 | params string[] directories 8 | ) 9 | { 10 | LoadHelper.FromDirectories(directories); 11 | return services; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Client/Grpc/Aoxe.Client.Grpc/Aoxe.Client.Grpc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Aoxe/AoxeHost.Extensions.UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe; 2 | 3 | public static partial class AoxeHostExtensions 4 | { 5 | public static AoxeHost AddAoxeUnitOfWork( 6 | this AoxeHost aoxeHost, 7 | Func factory 8 | ) 9 | { 10 | aoxeHost.AddScoped(factory); 11 | aoxeHost.Configure(app => app.UseAoxeUnitOfWork()); 12 | return aoxeHost; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/PubSub/Aoxe.PubSub.Distributed/Aoxe.PubSub.LocalMessageStorage.EntityFramework/Aoxe.PubSub.LocalMessageStorage.EntityFramework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latestmajor 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi/Aoxe.IServiceCollection.Extensions.UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.WebApi; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddUnitOfWork( 6 | this IServiceCollection services, 7 | Func factory 8 | ) 9 | { 10 | services.AddScoped(factory); 11 | return services; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo/Aoxe/CarolHost/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarolHost; 2 | 3 | public class Program 4 | { 5 | public static void Main(string[] args) => 6 | Host.CreateDefaultBuilder(args) 7 | .ConfigureWebHostDefaults(webBuilder => 8 | { 9 | webBuilder.UseStartup(); 10 | webBuilder.UseUrls("http://localhost:5003"); 11 | }) 12 | .Build() 13 | .Run(); 14 | } 15 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/GlobalUsing.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Net.Http; 4 | global using System.Text; 5 | global using System.Threading.Tasks; 6 | global using Aoxe.AspNetCore.Formatters.Demo; 7 | global using Aoxe.Serializer.Abstractions; 8 | global using Jil; 9 | global using Microsoft.AspNetCore.Hosting; 10 | global using Microsoft.AspNetCore.TestHost; 11 | global using Xunit; 12 | -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier.Abstractions/BusinessLogic/MessageHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.ThreeTier.Abstractions.BusinessLogic; 2 | 3 | public interface IMessageHandler { } 4 | 5 | public interface IMessageHandler : IMessageHandler 6 | { 7 | void Handle(TMessage message); 8 | } 9 | 10 | public class MessageHandlerAttribute(string handleName) : Attribute 11 | { 12 | public string HandleName { get; } = handleName.Trim(); 13 | } 14 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/ServiceHost/UowMiddleware.cs: -------------------------------------------------------------------------------- 1 | namespace ServiceHost; 2 | 3 | public class UowMiddleware 4 | { 5 | private readonly RequestDelegate _next; 6 | 7 | public UowMiddleware(RequestDelegate next) 8 | { 9 | _next = next; 10 | } 11 | 12 | public async Task Invoke(HttpContext context, AoxeDddContext dbContext) 13 | { 14 | await _next(context); 15 | await dbContext.SaveChangesAsync(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Formatter/AoxeClientFormatterOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Formatter; 2 | 3 | public class AoxeClientFormatterOptions 4 | { 5 | public string MediaType { get; set; } 6 | public IStreamSerializer Serializer { get; set; } 7 | 8 | public AoxeClientFormatterOptions(IStreamSerializer serializer, string mediaType) 9 | { 10 | Serializer = serializer; 11 | MediaType = mediaType; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/BusinessLogicLayer/BusinessLogicLayer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Scheduling/Aoxe.Hangfire/Aoxe.Hangfire.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/IRepositories/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Aoxe.DDD.Abstractions.Infrastructure.Repository; 5 | using Domain.AggregateRoots; 6 | 7 | namespace Domain.IRepositories 8 | { 9 | public interface IUserRepository : IRepository 10 | { 11 | Task AddAsync(User user); 12 | Task GetAsync(Guid id); 13 | Task> GetAllAsync(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceHost/Program.cs: -------------------------------------------------------------------------------- 1 | namespace AliceHost; 2 | 3 | public class Program 4 | { 5 | public static void Main(string[] args) 6 | { 7 | var builder = Host.CreateDefaultBuilder(args); 8 | builder 9 | .ConfigureWebHostDefaults(webBuilder => 10 | { 11 | webBuilder.UseStartup(); 12 | webBuilder.UseUrls("http://localhost:5001"); 13 | }) 14 | .Build() 15 | .Run(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Repository/Repository.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Infrastructure/Repository/Repository.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Infrastructure.Repository; 2 | 3 | public interface IRepository { } 4 | 5 | public interface IRepository : IRepository 6 | where TAggregateRoot : IAggregateRoot { } 7 | 8 | public interface IRepository : IRepository 9 | where TAggregateRoot : IAggregateRoot { } 10 | 11 | public class RepositoryAttribute : Attribute { } 12 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/WebApiHost/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System.Data; 2 | global using Aoxe.ThreeTier; 3 | global using Aoxe.ThreeTier.Abstractions.BusinessLogic; 4 | global using Aoxe.WebApi; 5 | global using Microsoft.AspNetCore.Builder; 6 | global using Microsoft.AspNetCore.Hosting; 7 | global using Microsoft.Extensions.Configuration; 8 | global using Microsoft.Extensions.DependencyInjection; 9 | global using Microsoft.Extensions.Hosting; 10 | global using Microsoft.OpenApi.Models; 11 | global using Npgsql; 12 | -------------------------------------------------------------------------------- /src/Client/Http/Aoxe.Client.Http/Internal/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Internal; 2 | 3 | internal static class StringExtensions 4 | { 5 | internal static string TrimEnd(this string target, string trimString) 6 | { 7 | if (string.IsNullOrEmpty(trimString)) 8 | return target; 9 | 10 | var result = target; 11 | while (result.EndsWith(trimString)) 12 | result = result[..^trimString.Length]; 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Client/Http/Aoxe.Client.Http/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Linq; 4 | global using System.Net; 5 | global using System.Net.Http; 6 | global using System.Reflection; 7 | global using System.Threading; 8 | global using System.Threading.Tasks; 9 | global using Aoxe.Client.Http.Formatter; 10 | global using Aoxe.Client.Http.Internal; 11 | global using Aoxe.Core; 12 | global using Aoxe.Shared; 13 | global using Microsoft.Extensions.DependencyInjection; 14 | -------------------------------------------------------------------------------- /src/Aoxe/AoxeHost.Extensions.Client.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe; 2 | 3 | public static partial class AoxeHostExtensions 4 | { 5 | public static AoxeHost UseAoxeClient( 6 | this AoxeHost aoxeHost, 7 | Type serviceDefineType, 8 | Dictionary configUrls, 9 | Action? optionsFactory = null 10 | ) => 11 | aoxeHost.ConfigureServices( 12 | services => services.AddAoxeClient(serviceDefineType, configUrls, optionsFactory) 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /demo/Aoxe/BobSystem/IBobServices/IBobService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Interfaces; 4 | 5 | namespace IBobServices 6 | { 7 | public interface IBobService : IService 8 | { 9 | string Hello(); 10 | Task HelloAsyncTest(); 11 | string SayHelloToAlice(); 12 | Task SayHelloToAliceAsyncTest(); 13 | string SayHelloToCarol(); 14 | Exception ThrowException(); 15 | Task PassAppleToAliceAsync(string appleName); 16 | } 17 | } -------------------------------------------------------------------------------- /demo/Aoxe/CarolSystem/CarolServices/CarolServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/Entities/Card.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Aoxe.DDD.Abstractions.Domain; 3 | 4 | namespace Domain.Entities 5 | { 6 | public class Card : Entity 7 | { 8 | public Guid Id { get; protected set; } 9 | public string Name { get; protected set; } 10 | 11 | public Card(Guid id, string name) 12 | { 13 | if (id == Guid.Empty) 14 | throw new ArgumentNullException(nameof(id)); 15 | (Id, Name) = (id, name); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/DataAccessLayer/DataAccessLayer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Aoxe.Core/AoxeException.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Core; 2 | 3 | public class AoxeException : Exception 4 | { 5 | public Guid Id { get; set; } = Guid.NewGuid(); 6 | public string Code { get; set; } = string.Empty; 7 | public DateTime ThrowTime { get; set; } = DateTime.UtcNow; 8 | 9 | public AoxeException() { } 10 | 11 | public AoxeException(string message) 12 | : base(message) { } 13 | 14 | public AoxeException(string message, Exception innerException) 15 | : base(message, innerException) { } 16 | } 17 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/ValueObjects/Address.cs: -------------------------------------------------------------------------------- 1 | namespace Domain.ValueObjects 2 | { 3 | public record Address 4 | { 5 | public string Country { get; protected set; } 6 | public string State { get; protected set; } 7 | public string City { get; protected set; } 8 | public string Street { get; protected set; } 9 | 10 | public Address(string country, string state, string city, string street) => 11 | (Country, State, City, Street) = (country, state, city, street); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD/Aoxe.IServiceCollection.Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddDDD(this IServiceCollection services) => 6 | services 7 | .AddApplicationService() 8 | .AddIntegrationEventHandler() 9 | .AddDomainService() 10 | .AddDomainEventHandler() 11 | .AddDomainFactory() 12 | .AddRepository() 13 | .AddDomainEventPublisher(); 14 | } 15 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceSystem/IAliceServices/IAliceService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Interfaces; 4 | 5 | namespace IAliceServices 6 | { 7 | public interface IAliceService : IService 8 | { 9 | string Hello(); 10 | string SayHelloToBob(); 11 | string SayHellosToBob(int quantity); 12 | Task SayHelloToBobAsyncTest(); 13 | string SayHelloToCarol(); 14 | Exception ThrowException(); 15 | Task PassBackAppleAsync(Apple apple); 16 | Task HelloAsyncTest(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.MsgPack/AoxeClientFormatterOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.MsgPack; 2 | 3 | public static class AoxeClientFormatterOptionsExtensions 4 | { 5 | public static AoxeClientFormatterOptions UseMsgPackFormatter( 6 | this AoxeClientFormatterOptions formatterOptions, 7 | string mediaType = "application/x-msgpack" 8 | ) 9 | { 10 | formatterOptions.Serializer = new Aoxe.MsgPack.Serializer(); 11 | formatterOptions.MediaType = mediaType; 12 | return formatterOptions; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceSystem/AliceServices/AliceServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Protobuf/AoxeClientFormatterOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Protobuf; 2 | 3 | public static class AoxeClientFormatterOptionsExtensions 4 | { 5 | public static AoxeClientFormatterOptions UseProtobufFormatter( 6 | this AoxeClientFormatterOptions formatterOptions, 7 | string mediaType = "application/x-protobuf" 8 | ) 9 | { 10 | formatterOptions.Serializer = new Aoxe.Protobuf.Serializer(); 11 | formatterOptions.MediaType = mediaType; 12 | return formatterOptions; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace BackendForBrowser 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Domain/DomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Domain; 2 | 3 | public interface IDomainEventHandler { } 4 | 5 | public interface IDomainEventHandler : IDomainEventHandler 6 | where TDomainEvent : IDomainEvent 7 | { 8 | void Handle(TDomainEvent domainEvent); 9 | } 10 | 11 | public class DomainEventHandlerAttribute : Attribute 12 | { 13 | public string HandleName { get; } 14 | 15 | public DomainEventHandlerAttribute(string handleName) 16 | { 17 | HandleName = handleName.Trim(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Zeroformatter/AoxeClientFormatterOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Zeroformatter; 2 | 3 | public static class AoxeClientFormatterOptionsExtensions 4 | { 5 | public static AoxeClientFormatterOptions UseZeroFormatter( 6 | this AoxeClientFormatterOptions formatterOptions, 7 | string mediaType = "application/x-zeroformatter" 8 | ) 9 | { 10 | formatterOptions.Serializer = new ZeroFormatter.Serializer(); 11 | formatterOptions.MediaType = mediaType; 12 | return formatterOptions; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi/UnitOfWorkMiddleware.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.WebApi; 2 | 3 | public class UnitOfWorkMiddleware 4 | { 5 | private readonly RequestDelegate _next; 6 | 7 | public UnitOfWorkMiddleware(RequestDelegate next) => _next = next; 8 | 9 | public async Task Invoke(HttpContext context, IDbTransaction dbTransaction) 10 | { 11 | await _next(context); 12 | try 13 | { 14 | dbTransaction.Commit(); 15 | } 16 | catch 17 | { 18 | dbTransaction.Rollback(); 19 | throw; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Application/README.md: -------------------------------------------------------------------------------- 1 | # 应用层 2 | 3 | --- 4 | 5 | ## 构成 6 | 7 | * 应用服务 8 | * 集成事件订阅者 9 | * 集成事件发布者 10 | 11 | ## 说明 12 | 13 | * 应用层作为界限上下文(Bounded context)的皮肤,是很薄的一层,本身不存在任何的业务逻辑。 14 | * 每一个应用服务的方法都是一个用例(use case),所以一个请求对应一个方法。 15 | * 集成事件源自领域事件,当领域事件需要发布到微服务之外,则在应用层实现一个领域事件处理者,将其转换为集成事件再发布出去。 16 | * 应用层同时负责订阅外界的集成事件,但应用服务和集成事件订阅者不过是触发方式(请求响应/发布订阅)的不同,它们要遵守的原则实际上差不多。 17 | * **注意**:因为每个应用服务的方法都对应一个用例,因此同一个界限上下文中的应用服务不应该存在相互调用的情况,因为: 18 | 1. 如果存在相互调用,则随着系统的迭代最终会让应用服务层形成一个网状依赖的结构,从而提高了维护成本。 19 | 2. 应用服务间存在相互调用,代表着用例的复用,这不仅是破坏了用例的定义,某种程度上还相当于将业务逻辑泄露到应用层,因为这导致应用层中存在对业务逻辑的编排。 20 | 21 | -------------------------------------------------------------------------------- /src/Aoxe.Core/AoxeUnpublishedMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Core; 2 | 3 | public record AoxeUnpublishedMessage : AoxeMessage 4 | { 5 | public DateTime PersistenceUtcTime { get; protected set; } = DateTime.UtcNow; 6 | 7 | private AoxeUnpublishedMessage() { } 8 | 9 | public AoxeUnpublishedMessage( 10 | Guid id, 11 | string messageType, 12 | string content, 13 | DateTime persistenceUtcTime 14 | ) 15 | { 16 | Id = id; 17 | MessageType = messageType; 18 | Content = content; 19 | PersistenceUtcTime = persistenceUtcTime; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Jil/AoxeClientFormatterOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Jil; 2 | 3 | public static class AoxeClientFormatterOptionsExtensions 4 | { 5 | public static AoxeClientFormatterOptions UseJilFormatter( 6 | this AoxeClientFormatterOptions formatterOptions, 7 | string mediaType = "application/x-jil", 8 | Options? jilOptions = null 9 | ) 10 | { 11 | formatterOptions.Serializer = new Aoxe.Jil.Serializer(jilOptions); 12 | formatterOptions.MediaType = mediaType; 13 | return formatterOptions; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/MsgPack.Formatter.Test.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Tests; 2 | 3 | public partial class FormatterTest 4 | { 5 | [Fact] 6 | public async Task MsgPackFormatterTest() 7 | { 8 | var serializer = new Aoxe.MsgPack.Serializer(); 9 | await StreamFormatterAsync(serializer, "application/x-msgpack"); 10 | } 11 | 12 | [Fact] 13 | public async Task MsgPackFormatterNullTest() 14 | { 15 | var serializer = new Aoxe.MsgPack.Serializer(); 16 | await StreamFormatterNullAsync(serializer, "application/x-msgpack"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi/AoxeAppServiceControllerFeatureProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.WebApi; 2 | 3 | internal class AoxeAppServiceControllerFeatureProvider 4 | : IApplicationFeatureProvider 5 | { 6 | private readonly List _implementTypes; 7 | 8 | public AoxeAppServiceControllerFeatureProvider(List implementTypes) => 9 | _implementTypes = implementTypes; 10 | 11 | public void PopulateFeature(IEnumerable parts, ControllerFeature feature) => 12 | _implementTypes.ForEach(serviceType => feature.Controllers.Add(serviceType.GetTypeInfo())); 13 | } 14 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/Utf8Json.Formatter.Test.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Tests; 2 | 3 | public partial class FormatterTest 4 | { 5 | [Fact] 6 | public async Task Utf8JsonFormatterTest() 7 | { 8 | var serializer = new Aoxe.Utf8Json.Serializer(); 9 | await TextFormatterAsync(serializer, "application/x-utf8json"); 10 | } 11 | 12 | [Fact] 13 | public async Task Utf8JsonFormatterNullTest() 14 | { 15 | var serializer = new Aoxe.Utf8Json.Serializer(); 16 | await TextFormatterNullAsync(serializer, "application/x-utf8json"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/ConsoleHost/ConsoleHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/Protobuf.Formatter.Test.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Tests; 2 | 3 | public partial class FormatterTest 4 | { 5 | [Fact] 6 | public async Task ProtobufFormatterTest() 7 | { 8 | var serializer = new Aoxe.Protobuf.Serializer(); 9 | await StreamFormatterAsync(serializer, "application/x-protobuf"); 10 | } 11 | 12 | [Fact] 13 | public async Task ProtobufFormatterNullTest() 14 | { 15 | var serializer = new Aoxe.Protobuf.Serializer(); 16 | await StreamFormatterNullAsync(serializer, "application/x-protobuf"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/ZeroFormatter.Formatter.Test.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Tests; 2 | 3 | public partial class FormatterTest 4 | { 5 | [Fact] 6 | public async Task ZeroFormatterTest() 7 | { 8 | var serializer = new Aoxe.ZeroFormatter.Serializer(); 9 | await StreamFormatterAsync(serializer, "application/x-zeroformatter"); 10 | } 11 | 12 | [Fact] 13 | public async Task ZeroFormatterNullTest() 14 | { 15 | var serializer = new Aoxe.ZeroFormatter.Serializer(); 16 | await StreamFormatterNullAsync(serializer, "application/x-zeroformatter"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/Aoxe/BobSystem/BobServices/BobServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aoxe 2 | 3 | A micro service framework base by asp.net core. 4 | 5 | [Aoxe.DDD](https://github.com/AoxeTech/Aoxe/tree/master/src/DDD) 6 | 7 | [Aoxe.ThreeTier](https://github.com/AoxeTech/Aoxe/tree/master/src/ThreeTier) 8 | 9 | Thank`s for [JetBrains](https://www.jetbrains.com/) for the great support in providing assistance and user-friendly environment for my open source projects. 10 | 11 | [![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg?_gl=1*f25lxa*_ga*MzI3ODk2MjY0LjE2NzA0NjY4MDQ.*_ga_9J976DJZ68*MTY4OTY4NzY5OS4zNC4xLjE2ODk2ODgwMDAuNTMuMC4w)](https://www.jetbrains.com/community/opensource/#support) -------------------------------------------------------------------------------- /src/Aoxe/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Data; 4 | global using System.Linq; 5 | global using System.Reflection; 6 | global using Aoxe.Client.Http; 7 | global using Aoxe.Client.Http.Formatter; 8 | global using Aoxe.DDD; 9 | global using Aoxe.Server; 10 | global using Aoxe.Shared; 11 | global using Aoxe.ThreeTier; 12 | global using Microsoft.AspNetCore.Builder; 13 | global using Microsoft.AspNetCore.Hosting; 14 | global using Microsoft.Extensions.DependencyInjection; 15 | global using Microsoft.Extensions.DependencyInjection.Extensions; 16 | global using Microsoft.Extensions.Hosting; 17 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Application/IntegrationEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Application; 2 | 3 | public interface IIntegrationEventHandler { } 4 | 5 | public interface IIntegrationEventHandler : IIntegrationEventHandler 6 | where TIntegrationEvent : IIntegrationEvent 7 | { 8 | void Handle(TIntegrationEvent integrationEvent); 9 | } 10 | 11 | public class IntegrationEventHandlerAttribute : Attribute 12 | { 13 | public string HandleName { get; } 14 | 15 | public IntegrationEventHandlerAttribute(string handleName) 16 | { 17 | HandleName = handleName; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.MsgPack/README.md: -------------------------------------------------------------------------------- 1 | # Aoxe.AspNetCore.MsgPack 2 | 3 | Protobuf formatters for asp.net core 4 | 5 | ## QuickStart 6 | 7 | ### NuGet 8 | 9 | Install-Package Aoxe.AspNetCore.Formatters.MsgPack 10 | 11 | ### Build Project 12 | 13 | Create an asp.net core project and import reference in startup.cs 14 | 15 | ```CSharp 16 | using Aoxe.AspNetCore.Formatters.MsgPack; 17 | ``` 18 | 19 | Modify the ConfigureServices like this 20 | 21 | ```CSharp 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers() 25 | .AddMsgPack(); 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Protobuf/README.md: -------------------------------------------------------------------------------- 1 | # Aoxe.AspNetCore.Protobuf 2 | 3 | Protobuf formatters for asp.net core 4 | 5 | ## QuickStart 6 | 7 | ### NuGet 8 | 9 | Install-Package Aoxe.AspNetCore.Formatters.Protobuf 10 | 11 | ### Build Project 12 | 13 | Create an asp.net core project and import reference in startup.cs 14 | 15 | ```CSharp 16 | using Aoxe.AspNetCore.Formatters.Protobuf; 17 | ``` 18 | 19 | Modify the ConfigureServices like this 20 | 21 | ```CSharp 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers() 25 | .AddProtobuf(); 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Utf8Json/README.md: -------------------------------------------------------------------------------- 1 | # Aoxe.AspNetCore.Utf8Json 2 | 3 | Utf8Json formatters for asp.net core 4 | 5 | ## QuickStart 6 | 7 | ### NuGet 8 | 9 | Install-Package Aoxe.AspNetCore.Formatters.Utf8Json 10 | 11 | ### Build Project 12 | 13 | Create an asp.net core project and import reference in startup.cs 14 | 15 | ```CSharp 16 | using Aoxe.AspNetCore.Formatters.Utf8Json; 17 | ``` 18 | 19 | Modify the ConfigureServices like this 20 | 21 | ```CSharp 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers() 25 | .AddUtf8Json(); 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/WebApiHost/WebApiHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Aoxe.Shared/LoadHelper.LoadByAssemblies.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Shared; 2 | 3 | public static partial class LoadHelper 4 | { 5 | public static void FromAssemblyNames(params AssemblyName[] assemblyNames) => 6 | FromAssemblies(assemblyNames.Select(Assembly.Load).ToArray()); 7 | 8 | public static void FromAssemblies(params Assembly[] assemblies) 9 | { 10 | SpecifyTypes.AddRange( 11 | assemblies 12 | .SelectMany(assembly => assembly.GetTypes()) 13 | .Where(p => !SpecifyTypes.Contains(p)) 14 | .Distinct() 15 | ); 16 | LoadMode = LoadTypesMode.LoadBySpecify; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Utf8Json/AoxeClientFormatterOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Utf8Json; 2 | 3 | public static class AoxeClientFormatterOptionsExtensions 4 | { 5 | public static AoxeClientFormatterOptions UseUtf8JsonFormatter( 6 | this AoxeClientFormatterOptions formatterOptions, 7 | string mediaType = "application/x-utf8json", 8 | IJsonFormatterResolver? jsonFormatterResolver = null 9 | ) 10 | { 11 | formatterOptions.Serializer = new Aoxe.Utf8Json.Serializer(jsonFormatterResolver); 12 | formatterOptions.MediaType = mediaType; 13 | return formatterOptions; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier/Aoxe.IServiceCollection.Extensions.Bll.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.ThreeTier; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddBll(this IServiceCollection services) => 6 | services 7 | .Register(ServiceLifetime.Scoped) 8 | .Register(ServiceLifetime.Scoped); 9 | 10 | public static IServiceCollection AddMessageHandler(this IServiceCollection services) => 11 | services 12 | .Register(ServiceLifetime.Scoped) 13 | .Register(ServiceLifetime.Scoped); 14 | } 15 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/ServiceHost/ServiceCollectionExtension.cs: -------------------------------------------------------------------------------- 1 | namespace ServiceHost; 2 | 3 | public static class ServiceCollectionExtension 4 | { 5 | public static IServiceCollection AddDbContext( 6 | this IServiceCollection services, 7 | IConfiguration config 8 | ) 9 | { 10 | //注册EF用于C端仓储层 11 | services.AddDbContext( 12 | options => options.UseNpgsql(config.GetSection("PgSqlPrimary").Get()) 13 | ); 14 | //使用上面已注册的pgsql上下文再次注册DbContext以用于框架内注入提交UOW 15 | services.AddScoped(p => p.GetService()); 16 | return services; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.ZeroFormatter/README.md: -------------------------------------------------------------------------------- 1 | # Aoxe.AspNetCore.ZeroFormatter 2 | 3 | Protobuf formatters for asp.net core 4 | 5 | ## QuickStart 6 | 7 | ### NuGet 8 | 9 | Install-Package Aoxe.AspNetCore.Formatters.ZeroFormatter 10 | 11 | ### Build Project 12 | 13 | Create an asp.net core project and import reference in startup.cs 14 | 15 | ```CSharp 16 | using Aoxe.AspNetCore.Formatters.ZeroFormatter; 17 | ``` 18 | 19 | Modify the ConfigureServices like this 20 | 21 | ```CSharp 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers() 25 | .AddZeroFormatter(); 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Domain/DomainEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Domain; 2 | 3 | public interface IDomainEvent : IValueObject { } 4 | 5 | public interface IDomainEvent : IDomainEvent 6 | { 7 | TEntityId EntityId { get; } 8 | public int EntityVersion { get; } 9 | } 10 | 11 | public abstract record DomainEvent : IDomainEvent 12 | { 13 | public TEntityId EntityId { get; protected set; } 14 | public int EntityVersion { get; protected set; } 15 | 16 | protected DomainEvent(Entity entity) 17 | { 18 | EntityId = entity.Id; 19 | EntityVersion = entity.Version; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi/Aoxe.IApplicationBuilder.Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.WebApi; 2 | 3 | public static class AoxeIApplicationBuilderExtensions 4 | { 5 | public static IApplicationBuilder UseAoxe(this IApplicationBuilder app) => 6 | app.UseAoxeErrorHandling() 7 | .UseRouting() 8 | .UseEndpoints(endpoints => endpoints.MapControllers()); 9 | 10 | public static IApplicationBuilder UseAoxeErrorHandling(this IApplicationBuilder app) => 11 | app.UseMiddleware(); 12 | 13 | public static IApplicationBuilder UseAoxeUnitOfWork(this IApplicationBuilder app) => 14 | app.UseMiddleware(); 15 | } 16 | -------------------------------------------------------------------------------- /src/Aoxe.Shared/Aoxe.IServiceCollection.Extensions.LoadByAssemblies.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Shared; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection FromAssemblyNames( 6 | this IServiceCollection services, 7 | params AssemblyName[] assemblyNames 8 | ) 9 | { 10 | LoadHelper.FromAssemblyNames(assemblyNames); 11 | return services; 12 | } 13 | 14 | public static IServiceCollection FromAssemblies( 15 | this IServiceCollection services, 16 | params Assembly[] assemblies 17 | ) 18 | { 19 | LoadHelper.FromAssemblies(assemblies); 20 | return services; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Aoxe.Core/AoxePublishedMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Core; 2 | 3 | public record AoxePublishedMessage : AoxeMessage 4 | { 5 | public DateTime PersistenceUtcTime { get; protected set; } = DateTime.UtcNow; 6 | public DateTime PublishedUtcTime { get; protected set; } = DateTime.UtcNow; 7 | 8 | private AoxePublishedMessage() { } 9 | 10 | public AoxePublishedMessage(AoxeUnpublishedMessage unpublishedMessage) 11 | { 12 | Id = unpublishedMessage.Id; 13 | MessageType = unpublishedMessage.MessageType; 14 | Content = unpublishedMessage.Content; 15 | PersistenceUtcTime = unpublishedMessage.PersistenceUtcTime; 16 | PublishedUtcTime = DateTime.UtcNow; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/Aoxe/BobHost/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using System.Collections.Generic; 4 | global using Aoxe; 5 | global using Aoxe.AspNetCore.Formatters.Jil; 6 | global using Aoxe.AspNetCore.Formatters.MsgPack; 7 | global using Aoxe.AspNetCore.Formatters.Protobuf; 8 | global using Aoxe.AspNetCore.Formatters.Utf8Json; 9 | global using Aoxe.AspNetCore.Formatters.ZeroFormatter; 10 | global using Aoxe.Client.Http.MsgPack; 11 | global using Aoxe.Server; 12 | global using BobServices; 13 | global using IAliceServices; 14 | global using IBobServices; 15 | global using ICarolServices; 16 | global using Interfaces; 17 | global using Microsoft.AspNetCore.Builder; 18 | global using Microsoft.Extensions.DependencyInjection; 19 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD/Aoxe.IServiceCollection.Extensions.Application.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddApplicationService(this IServiceCollection services) => 6 | services 7 | .Register(ServiceLifetime.Scoped) 8 | .Register(ServiceLifetime.Scoped); 9 | 10 | public static IServiceCollection AddIntegrationEventHandler(this IServiceCollection services) => 11 | services 12 | .Register(ServiceLifetime.Scoped) 13 | .Register(ServiceLifetime.Scoped); 14 | } 15 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Linq; 4 | global using System.Threading; 5 | global using System.Threading.Tasks; 6 | global using Aoxe.Core; 7 | global using Aoxe.DDD.Abstractions.Application; 8 | global using Aoxe.DDD.Abstractions.Domain; 9 | global using Aoxe.DDD.Abstractions.Infrastructure.Messaging; 10 | global using Aoxe.DDD.Abstractions.Infrastructure.Repository; 11 | global using Aoxe.SequentialGuid; 12 | global using Aoxe.Serializer.Abstractions; 13 | global using Aoxe.Shared; 14 | global using Microsoft.EntityFrameworkCore; 15 | global using Microsoft.Extensions.DependencyInjection; 16 | global using Microsoft.Extensions.Hosting; 17 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/README.md: -------------------------------------------------------------------------------- 1 | # Frontend 2 | 3 | --- 4 | 5 | ## 说明 6 | 7 | * 此模块包含了BFF和QueryService,QueryService是一个被BFF依赖的类库。但在拓扑中,两者可以是单独的模块。 8 | * 此示例是通过BFF直接调用QueryService完成Q端功能,假如是独立的QueryService,其本身也可以是一个微服务,BFF通过GraphQL等技术手段进行远程调用。 9 | * 从团队组成的职责来说,BFF是由负责对应客户端的前端编写的(如Browser团队负责BFF-Browser,IOS团队负责BFF-IOS,Android团队负责BFF-Android),其主要职责在于: 10 | 11 | 1. 响应的格式化:前端要求后端响应能够满足特定的屏幕/显示适配。 12 | 2. 完成一个页面操作需要调用多次API所导致的性能问题:从面向对象设计来说,相当于一种外观模式,实际上是一种为客户端而设计的API网关。 13 | 3. **解决以数据库为中心的API设计**:restful风格不仅仅是一种风格,其最重要的概念在于对**资源**的定义和抽象,实际上**资源**就是DDD中的**聚合**。 14 | * 从团队组成的职责来说,QueryService由后端或者BI负责编写: 15 | 16 | 1. CQS:可以通过GraphQL等技术手段给予前端数据查询的功能。 17 | 2. CQRS:每类请求都可以抽象为一个读模型,而读模型本身也是聚合的一种,通过空间换时间的方式提供成本更高但性能和扩展性更强的数据输出手段(如报表)。 18 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Repository/CustomDbContext.cs: -------------------------------------------------------------------------------- 1 | using Domain.AggregateRoots; 2 | using Microsoft.EntityFrameworkCore; 3 | using Repository.EntityConfigurations; 4 | using Aoxe.Serializer.Abstractions; 5 | using Aoxe.DDD; 6 | 7 | namespace Repository; 8 | 9 | public sealed class CustomDbContext : AoxeDddContext 10 | { 11 | public DbSet Users { get; set; } 12 | 13 | public CustomDbContext(DbContextOptions options, ITextSerializer serializer) : base(options, 14 | serializer) 15 | { 16 | Database.EnsureCreated(); 17 | } 18 | 19 | protected override void OnModelCreating(ModelBuilder modelBuilder) 20 | { 21 | modelBuilder.ApplyConfiguration(new UserEntityTypeConfiguration()); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Aoxe/AoxeHost.Extensions.HostService.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe; 2 | 3 | public partial class AoxeHost 4 | { 5 | public AoxeHost AddHostedService(AoxeHost aoxeHost) 6 | where THostedService : class, IHostedService 7 | { 8 | TryAddEnumerable(ServiceDescriptor.Singleton()); 9 | return aoxeHost; 10 | } 11 | 12 | public AoxeHost AddHostedService( 13 | AoxeHost aoxeHost, 14 | Func implementationFactory 15 | ) 16 | where THostedService : class, IHostedService 17 | { 18 | TryAddEnumerable(ServiceDescriptor.Singleton(implementationFactory)); 19 | return aoxeHost; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Formatter/AoxeHttpClientFormatterFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Formatter; 2 | 3 | public static class AoxeHttpClientFormatterFactory 4 | { 5 | public static AoxeHttpClientFormatter Create(AoxeClientFormatterOptions options) => 6 | options.Serializer switch 7 | { 8 | ITextSerializer => new AoxeHttpClientTextFormatter(options), 9 | not null => new AoxeHttpClientStreamFormatter(options), 10 | _ 11 | => throw new ArgumentOutOfRangeException( 12 | nameof(options.Serializer), 13 | $"options.Serializer must be {nameof(ITextSerializer)} or {nameof(IStreamSerializer)}." 14 | ) 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters/AoxeOutputFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters; 2 | 3 | public class AoxeOutputFormatter : OutputFormatter 4 | { 5 | private readonly IBytesSerializer _bytesSerializer; 6 | 7 | public AoxeOutputFormatter(MediaTypeHeaderValue contentType, IBytesSerializer bytesSerializer) 8 | { 9 | SupportedMediaTypes.Add(contentType); 10 | _bytesSerializer = bytesSerializer; 11 | } 12 | 13 | public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context) => 14 | await context 15 | .HttpContext 16 | .Response 17 | .BodyWriter 18 | .WriteAsync(_bytesSerializer.ToBytes(context.ObjectType!, context.Object)); 19 | } 20 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.ZeroFormatter/MvcBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.ZeroFormatter; 2 | 3 | public static class MvcBuilderExtension 4 | { 5 | public static IMvcBuilder AddZeroFormatter( 6 | this IMvcBuilder mvcBuilder, 7 | string contentType = "application/x-zeroformatter", 8 | string format = "zeroformatter" 9 | ) 10 | { 11 | if (string.IsNullOrWhiteSpace(contentType)) 12 | throw new ArgumentNullException(nameof(contentType)); 13 | if (string.IsNullOrWhiteSpace(format)) 14 | throw new ArgumentNullException(nameof(format)); 15 | 16 | return mvcBuilder.AddMvcOptions(options => options.AddZeroFormatter(contentType, format)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.MsgPack/MvcBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.MsgPack; 2 | 3 | public static class MvcBuilderExtension 4 | { 5 | public static IMvcBuilder AddMsgPack( 6 | this IMvcBuilder mvcBuilder, 7 | string contentType = "application/x-msgpack", 8 | string format = "msgpack" 9 | ) 10 | { 11 | if (string.IsNullOrWhiteSpace(contentType)) 12 | throw new ArgumentNullException(nameof(contentType)); 13 | if (string.IsNullOrWhiteSpace(format)) 14 | throw new ArgumentNullException(nameof(format)); 15 | 16 | return mvcBuilder.AddMvcOptions( 17 | options => options.AddMsgPackFormatter(contentType, format) 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Protobuf/MvcBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Protobuf; 2 | 3 | public static class MvcBuilderExtension 4 | { 5 | public static IMvcBuilder AddProtobuf( 6 | this IMvcBuilder mvcBuilder, 7 | string contentType = "application/x-protobuf", 8 | string format = "protobuf" 9 | ) 10 | { 11 | if (string.IsNullOrWhiteSpace(contentType)) 12 | throw new ArgumentNullException(nameof(contentType)); 13 | if (string.IsNullOrWhiteSpace(format)) 14 | throw new ArgumentNullException(nameof(format)); 15 | 16 | return mvcBuilder.AddMvcOptions( 17 | options => options.AddProtobufFormatter(contentType, format) 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Client/Http/Aoxe.Client.Http/Internal/TaskExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Internal; 2 | 3 | internal static class TaskExtensions 4 | { 5 | internal static Task CastResult(this Task taskResult, Type resultType) 6 | { 7 | var taskSource = new TaskCompletionSource(resultType); 8 | taskResult.ContinueWith(task => 9 | { 10 | try 11 | { 12 | taskSource.SetResult(task.Result); 13 | } 14 | catch (AggregateException ex) 15 | { 16 | taskSource.SetException(ex.InnerException!); 17 | } 18 | catch (Exception ex) 19 | { 20 | taskSource.SetException(ex); 21 | } 22 | }); 23 | return taskSource.Task; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Jil/README.md: -------------------------------------------------------------------------------- 1 | # Aoxe.AspNetCore.Jil 2 | 3 | Jil formatters for asp.net core 4 | 5 | ## QuickStart 6 | 7 | ### NuGet 8 | 9 | Install-Package Aoxe.AspNetCore.Formatters.Jil 10 | 11 | ### Build Project 12 | 13 | Create an asp.net core project and import reference in startup.cs 14 | 15 | ```CSharp 16 | using Aoxe.AspNetCore.Formatters.Jil; 17 | ``` 18 | 19 | Modify the ConfigureServices like this 20 | 21 | ```CSharp 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers() 25 | .AddJil(jilOptions: new Options(dateFormat: DateTimeFormat.ISO8601, 26 | excludeNulls: true, includeInherited: true, 27 | serializationNameFormat: SerializationNameFormat.CamelCase)); 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Jil/MvcBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Jil; 2 | 3 | public static class MvcBuilderExtension 4 | { 5 | public static IMvcBuilder AddJil( 6 | this IMvcBuilder mvcBuilder, 7 | string contentType = "application/x-jil", 8 | string format = "jil", 9 | Options? jilOptions = null 10 | ) 11 | { 12 | if (string.IsNullOrWhiteSpace(contentType)) 13 | throw new ArgumentNullException(nameof(contentType)); 14 | if (string.IsNullOrWhiteSpace(format)) 15 | throw new ArgumentNullException(nameof(format)); 16 | 17 | return mvcBuilder.AddMvcOptions( 18 | options => options.AddJilFormatter(contentType, format, jilOptions) 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Client/Http/Aoxe.Client.Http/Internal/AsyncExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Internal; 2 | 3 | internal static class AsyncExtensions 4 | { 5 | private static readonly TaskFactory TaskFactory = 6 | new( 7 | CancellationToken.None, 8 | TaskCreationOptions.None, 9 | TaskContinuationOptions.None, 10 | TaskScheduler.Default 11 | ); 12 | 13 | public static TResult? RunSync(this Task func) 14 | { 15 | return TaskFactory.StartNew(Func).Unwrap().GetAwaiter().GetResult(); 16 | Task Func() => func; 17 | } 18 | 19 | public static void RunSync(this Task func) 20 | { 21 | TaskFactory.StartNew(Func).Unwrap().GetAwaiter().GetResult(); 22 | return; 23 | Task Func() => func; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using Aoxe.AspNetCore.Formatters.Jil; 4 | global using Aoxe.AspNetCore.Formatters.MsgPack; 5 | global using Aoxe.AspNetCore.Formatters.Protobuf; 6 | global using Aoxe.AspNetCore.Formatters.Utf8Json; 7 | global using Aoxe.AspNetCore.Formatters.ZeroFormatter; 8 | global using Jil; 9 | global using Microsoft.AspNetCore.Builder; 10 | global using Microsoft.AspNetCore.Hosting; 11 | global using Microsoft.AspNetCore.Mvc; 12 | global using Microsoft.Extensions.Configuration; 13 | global using Microsoft.Extensions.DependencyInjection; 14 | global using Microsoft.Extensions.Hosting; 15 | global using Microsoft.Extensions.Primitives; 16 | global using Microsoft.Net.Http.Headers; 17 | global using ProtoBuf; 18 | global using ZeroFormatter; 19 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters/StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters; 2 | 3 | public static class StreamExtensions 4 | { 5 | public static async Task ReadToEndAsync( 6 | this Stream? stream, 7 | CancellationToken cancellationToken = default 8 | ) 9 | { 10 | switch (stream) 11 | { 12 | case null: 13 | return Array.Empty(); 14 | case MemoryStream ms: 15 | return ms.ToArray(); 16 | default: 17 | await using (var memoryStream = new MemoryStream()) 18 | { 19 | await stream.CopyToAsync(memoryStream, cancellationToken); 20 | return memoryStream.ToArray(); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/TestDto.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Demo; 2 | 3 | [ProtoContract] 4 | [ZeroFormattable] 5 | public class TestDto 6 | { 7 | [ProtoMember(1)] 8 | [Index(0)] 9 | public virtual Guid Id { get; set; } 10 | 11 | [ProtoMember(2)] 12 | [Index(1)] 13 | public virtual string? Name { get; set; } 14 | 15 | [ProtoMember(3)] 16 | [Index(2)] 17 | public virtual DateTime CreateTime { get; set; } 18 | 19 | [ProtoMember(4)] 20 | [Index(3)] 21 | public virtual long Tag { get; set; } 22 | 23 | [ProtoMember(5)] 24 | [Index(4)] 25 | public virtual TestEnum Enum { get; set; } 26 | // [ProtoMember(6)] [Index(5)] public virtual List Kids { get; set; } = new List(); 27 | // [ProtoMember(7)] public DateTimeOffset TestTime { get; set; } 28 | } 29 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Domain/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD.Abstractions.Domain; 2 | 3 | public interface IEntity { } 4 | 5 | public interface IEntity : IEntity 6 | { 7 | TId Id { get; } 8 | } 9 | 10 | public abstract class Entity : IEntity 11 | { 12 | public int Version { get; private set; } 13 | 14 | private readonly List _domainEvents = new(); 15 | public IReadOnlyList DomainEvents => _domainEvents; 16 | 17 | public void ClearDomainEvents() => _domainEvents.Clear(); 18 | 19 | protected void PublishEvent(Func domainEventFunc) 20 | { 21 | Version++; 22 | _domainEvents.Add(domainEventFunc()); 23 | } 24 | } 25 | 26 | public abstract class Entity : Entity, IEntity 27 | { 28 | public TId Id { get; protected set; } = default!; 29 | } 30 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Utf8Json/MvcBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Utf8Json; 2 | 3 | public static class MvcBuilderExtension 4 | { 5 | public static IMvcBuilder AddUtf8Json( 6 | this IMvcBuilder mvcBuilder, 7 | string contentType = "application/x-utf8json", 8 | string format = "utf8json", 9 | IJsonFormatterResolver? resolver = null 10 | ) 11 | { 12 | if (string.IsNullOrWhiteSpace(contentType)) 13 | throw new ArgumentNullException(nameof(contentType)); 14 | if (string.IsNullOrWhiteSpace(format)) 15 | throw new ArgumentNullException(nameof(format)); 16 | 17 | return mvcBuilder.AddMvcOptions( 18 | options => options.AddUtf8JsonFormatter(contentType, format, resolver) 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceHost/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using System.Collections.Generic; 4 | global using AliceServices; 5 | global using Aoxe.AspNetCore.Formatters.Jil; 6 | global using Aoxe.AspNetCore.Formatters.MsgPack; 7 | global using Aoxe.AspNetCore.Formatters.Protobuf; 8 | global using Aoxe.AspNetCore.Formatters.Utf8Json; 9 | global using Aoxe.AspNetCore.Formatters.ZeroFormatter; 10 | global using Aoxe.Client.Http; 11 | global using Aoxe.Client.Http.Jil; 12 | global using Aoxe.Shared; 13 | global using Aoxe.WebApi; 14 | global using IAliceServices; 15 | global using IBobServices; 16 | global using ICarolServices; 17 | global using Interfaces; 18 | global using Microsoft.AspNetCore.Builder; 19 | global using Microsoft.AspNetCore.Hosting; 20 | global using Microsoft.Extensions.DependencyInjection; 21 | global using Microsoft.Extensions.Hosting; 22 | -------------------------------------------------------------------------------- /demo/Aoxe/CarolHost/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using System.Collections.Generic; 4 | global using Aoxe.AspNetCore.Formatters.Jil; 5 | global using Aoxe.AspNetCore.Formatters.MsgPack; 6 | global using Aoxe.AspNetCore.Formatters.Protobuf; 7 | global using Aoxe.AspNetCore.Formatters.Utf8Json; 8 | global using Aoxe.AspNetCore.Formatters.ZeroFormatter; 9 | global using Aoxe.Client.Http; 10 | global using Aoxe.Client.Http.Utf8Json; 11 | global using Aoxe.Shared; 12 | global using Aoxe.WebApi; 13 | global using CarolServices; 14 | global using IAliceServices; 15 | global using IBobServices; 16 | global using ICarolServices; 17 | global using Interfaces; 18 | global using Microsoft.AspNetCore.Builder; 19 | global using Microsoft.AspNetCore.Hosting; 20 | global using Microsoft.Extensions.DependencyInjection; 21 | global using Microsoft.Extensions.Hosting; 22 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/QueryService/UserQueryService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Threading.Tasks; 5 | using Dapper; 6 | using QueryService.Models; 7 | 8 | namespace QueryService 9 | { 10 | public class UserQueryService 11 | { 12 | private readonly IDbConnection _dbConnection; 13 | 14 | public UserQueryService(IDbConnection dbConnection) 15 | { 16 | _dbConnection = dbConnection; 17 | } 18 | 19 | public async Task> GetByName(string name) 20 | { 21 | if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); 22 | return await _dbConnection.QueryAsync("SELECT * FROM User WHERE Name=@Name", 23 | new { Name = name }); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/DomainEvents/UserCreatedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Domain.AggregateRoots; 3 | using Aoxe.SequentialGuid; 4 | using Aoxe.DDD.Abstractions.Domain; 5 | 6 | namespace Domain.DomainEvents 7 | { 8 | public record UserCreatedEvent : IDomainEvent 9 | { 10 | public Guid Id { get; private set; } 11 | public Guid EntityId { get; } 12 | public int EntityVersion { get; } 13 | public DateTime EventTime { get; private set; } 14 | public User User { get; private set; } 15 | 16 | public UserCreatedEvent(User user) 17 | { 18 | Id = SequentialGuidHelper.GenerateComb(); 19 | User = user ?? throw new ArgumentNullException(nameof(user)); 20 | EntityId = user.Id; 21 | EntityVersion = user.Version; 22 | EventTime = DateTime.UtcNow; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Repository/README.md: -------------------------------------------------------------------------------- 1 | # 仓储层 2 | 3 | --- 4 | 5 | ## 构成 6 | 7 | * 仓储实现 8 | * 持久化对象(persistent object) 9 | 10 | ## 说明 11 | 12 | * 仓储层用于实现领域层中的仓储接口(repository interface)。 13 | * 仓储层与DAL是两种不同的设计思路,其区别在于: 14 | 1. 只有聚合(Aggregate)才有仓储。 15 | 2. 仓储是向上(领域层)服务,DAL是向下封装。 16 | * 最简单的仓储层只有Add和Get两个方法,分别是添加聚合以及依据Id获取聚合,原因是: 17 | 1. 聚合的Update应该使用状态跟踪功能来跟踪状态的变化,并在UOW提交的时候将新的状态持久化。 18 | 2. Delete是一种面向数据的思维,业务系统中其实不应该是**删除**而是**作废**,作废也属于状态变化的一种,至于作废后是软/硬删除还是归档,由业务/技术需求来处理。 19 | * 仓储的设计要依据命令查询分离(CQS)来处理,DDD只负责C端,所以仓储也只在C端,普通的查询(如列表页、报表等)应该在Q端的QueryService实现。 20 | * 此处仓储实现使用了EF Core,假如要替换数据库的话只需要修改EntityConfigurations,repository本身不需要修改。 21 | * 扩展: 22 | 1. 此处展示的是通过ORM(EF)实现的仓储层,如果使用ADO.NET或者Dapper之类的Micro ORM,除了仓储层实现外还有persistent object(PO)。 23 | 2. ORM实际上解决的是阻抗失配问题而不是数据库操作映射,因此不使用ORM时我们要手动解决阻抗失配,PO就是手段之一。 24 | 3. 将无法直接持久化的聚合转换为依据RDB范式设计的PO,再使用PO进行持久化操作,在这里PO是依据持久化技术选型而进行的聚合的状态的映射。 25 | 26 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/ServiceHost/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using System; 4 | global using System.Collections.Generic; 5 | global using System.Threading; 6 | global using System.Threading.Tasks; 7 | global using Aoxe.DDD; 8 | global using Aoxe.DDD.Abstractions.Infrastructure.Messaging; 9 | global using Aoxe.NewtonsoftJson; 10 | global using Aoxe.RabbitMQ.Abstractions; 11 | global using Aoxe.RabbitMQ.Client; 12 | global using Aoxe.Serializer.Abstractions; 13 | global using Microsoft.AspNetCore.Builder; 14 | global using Microsoft.AspNetCore.Hosting; 15 | global using Microsoft.AspNetCore.Http; 16 | global using Microsoft.EntityFrameworkCore; 17 | global using Microsoft.Extensions.Configuration; 18 | global using Microsoft.Extensions.DependencyInjection; 19 | global using Microsoft.Extensions.Hosting; 20 | global using Microsoft.OpenApi.Models; 21 | global using Repository; 22 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Concurrent; 3 | global using System.Collections.Generic; 4 | global using System.Data; 5 | global using System.Linq; 6 | global using System.Reflection; 7 | global using System.Text.Json; 8 | global using System.Threading.Tasks; 9 | global using Aoxe.Core; 10 | global using Aoxe.Shared; 11 | global using Microsoft.AspNetCore.Builder; 12 | global using Microsoft.AspNetCore.Http; 13 | global using Microsoft.AspNetCore.Mvc; 14 | global using Microsoft.AspNetCore.Mvc.ActionConstraints; 15 | global using Microsoft.AspNetCore.Mvc.ApplicationModels; 16 | global using Microsoft.AspNetCore.Mvc.ApplicationParts; 17 | global using Microsoft.AspNetCore.Mvc.Controllers; 18 | global using Microsoft.AspNetCore.Mvc.ModelBinding; 19 | global using Microsoft.AspNetCore.Mvc.Routing; 20 | global using Microsoft.Extensions.DependencyInjection; 21 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/BackendForBrowser.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/AspNetCoreFormatterTest.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Tests; 2 | 3 | public partial class FormatterTest 4 | { 5 | private readonly TestServer _server; 6 | 7 | public FormatterTest() 8 | { 9 | _server = new TestServer(new WebHostBuilder().UseStartup()); 10 | } 11 | 12 | private static HttpRequestMessage CreateHttpRequestMessage( 13 | HttpContent httpContent, 14 | string mediaType 15 | ) 16 | { 17 | var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "api/Values/Post") 18 | { 19 | Content = httpContent 20 | }; 21 | httpRequestMessage.Content.Headers.ContentType = 22 | new System.Net.Http.Headers.MediaTypeHeaderValue(mediaType); 23 | httpRequestMessage.Headers.Add("Accept", mediaType); 24 | 25 | return httpRequestMessage; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/README.md: -------------------------------------------------------------------------------- 1 | # 领域层 2 | 3 | --- 4 | 5 | ## 构成 6 | 7 | * 实体:以唯一标识(如Id)区分,唯一标识不同,即便所有属性相同,也会被认为是不同的实体。 8 | * 值对象:没有唯一标识,所有属性相同即可认为是同一个值对象。值对象是不可变类型。 9 | * 聚合:多个实体和值对象组成的高度相关的一个业务包装,同时作为一个数据修改的最小单元。 10 | * 领域服务:当一个用例横跨多个聚合时,由领域服务负责通过对聚合的编排组合完成用例。 11 | * 领域事件:在一个业务场景中一个聚合内所发生的事件。领域事件是一种值类型。 12 | * 仓储接口:用于定义聚合的持久化操作,只有聚合才有仓储。 13 | * 基础设施接口:如仓储层接口、事件总线、消息推送等,业务系统的核心是领域层,其它层都是围绕着为领域层服务。领域层需要什么,就在基础设施接口定义什么,基础设施层则依赖领域层并实现其需要的接口。 14 | * 工厂:为了保证聚合的原子性,当构建一个对象的过程很复杂,或者暴露过多的对象内部结构时,可以用工厂封装其构建过程便于调用方调用。 15 | 16 | ## 说明 17 | 18 | * 实体与值对象的所有属性均为public get/protected set,它们只能通过自己的方法改变自己的状态。 19 | * 聚合的原子性是DDD中第一原则,无论创建还是重建均要保证聚合的原子性。 20 | * 领域服务不要相互调用从而形成网状调用链,业务逻辑的复用应该实现在聚合而不是领域服务,领域服务最主要的职责是编排调用聚合从而完成use case。 21 | * 领域事件虽然是值类型,但也有Id用于基础设施的处理。 22 | * 聚合A可以作为参数传递到聚合B内,但因为聚合间是独立的关系因此聚合B内不能调用聚合A的方法。 23 | * 基础设施的设计方向与组件/工具类等相反,并不是组件/工具类有什么才提供什么封装,而是领域层中需要用到什么就直接定义基础设施接口,再由外界实现并通过接口注入。 24 | * 工厂一般是静态类,**其返回的聚合必定是业务完整而无需再进行处理**的。 -------------------------------------------------------------------------------- /demo/Aoxe/CarolSystem/CarolServices/CarolService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IAliceServices; 3 | using IBobServices; 4 | using ICarolServices; 5 | 6 | namespace CarolServices 7 | { 8 | public class CarolService : ICarolService 9 | { 10 | private readonly IAliceService _aliceService; 11 | private readonly IBobService _bobService; 12 | 13 | public CarolService(IAliceService aliceService, IBobService bobService) 14 | { 15 | _aliceService = aliceService; 16 | _bobService = bobService; 17 | } 18 | 19 | public string Hello() => "Hello,I am Carol."; 20 | 21 | public string SayHelloToAlice() => $"Hi,I am Carol.\r\n{_aliceService.Hello()}"; 22 | 23 | public string SayHelloToCarol() => $"Hi,I am Carol.\r\n{_bobService.Hello()}"; 24 | 25 | public Exception ThrowException() => throw new Exception("This exception was thrown by Bob."); 26 | } 27 | } -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/DomainEvents/UserNameChangedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Domain.AggregateRoots; 3 | using Aoxe.SequentialGuid; 4 | using Aoxe.DDD.Abstractions.Domain; 5 | 6 | namespace Domain.DomainEvents 7 | { 8 | public record UserNameChangedEvent : IDomainEvent 9 | { 10 | public Guid Id { get; private set; } 11 | public Guid EntityId { get; private set; } 12 | public int EntityVersion { get; } 13 | public DateTime EventTime { get; private set; } 14 | public string Name { get; private set; } 15 | 16 | public UserNameChangedEvent(User user) 17 | { 18 | if (user is null) throw new ArgumentNullException(nameof(user)); 19 | Id = SequentialGuidHelper.GenerateComb(); 20 | EntityId = user.Id; 21 | EntityVersion = user.Version; 22 | Name = user.Name; 23 | EventTime = DateTime.UtcNow; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/DomainEvents/UserBirthdayCelebratedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Domain.AggregateRoots; 3 | using Aoxe.SequentialGuid; 4 | using Aoxe.DDD.Abstractions.Domain; 5 | 6 | namespace Domain.DomainEvents 7 | { 8 | public record UserBirthdayCelebratedEvent : IDomainEvent 9 | { 10 | public Guid Id { get; private set; } 11 | public Guid EntityId { get; private set; } 12 | public int EntityVersion { get; } 13 | public DateTime EventTime { get; private set; } 14 | public int Age { get; private set; } 15 | 16 | public UserBirthdayCelebratedEvent(User user) 17 | { 18 | if (user is null) throw new ArgumentNullException(nameof(user)); 19 | Id = SequentialGuidHelper.GenerateComb(); 20 | EntityId = user.Id; 21 | EntityVersion = user.Version; 22 | Age = user.Age; 23 | EventTime = DateTime.UtcNow; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters/AoxeTextOutputFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters; 2 | 3 | public class AoxeTextOutputFormatter : TextOutputFormatter 4 | { 5 | private readonly ITextSerializer _textSerializer; 6 | 7 | public AoxeTextOutputFormatter( 8 | MediaTypeHeaderValue mediaTypeHeaderValue, 9 | ITextSerializer textSerializer 10 | ) 11 | { 12 | SupportedEncodings.Add(Encoding.UTF8); 13 | SupportedEncodings.Add(Encoding.Unicode); 14 | SupportedMediaTypes.Add(mediaTypeHeaderValue); 15 | _textSerializer = textSerializer; 16 | } 17 | 18 | public override async Task WriteResponseBodyAsync( 19 | OutputFormatterWriteContext context, 20 | Encoding selectedEncoding 21 | ) => 22 | await context 23 | .HttpContext 24 | .Response 25 | .WriteAsync(_textSerializer.ToText(context.ObjectType!, context.Object)); 26 | } 27 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Formatter/AoxeHttpClientFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Formatter; 2 | 3 | public abstract class AoxeHttpClientFormatter 4 | { 5 | internal static HttpRequestMessage CreateHttpRequestMessage( 6 | HttpContent httpContent, 7 | string mediaType, 8 | string requestUri 9 | ) 10 | { 11 | var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri) 12 | { 13 | Content = httpContent 14 | }; 15 | httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue(mediaType); 16 | httpRequestMessage.Headers.Add("Accept", mediaType); 17 | return httpRequestMessage; 18 | } 19 | 20 | public abstract HttpRequestMessage CreateHttpRequestMessage(string requestUri, object? message); 21 | public abstract Task GetResultAsync( 22 | Type returnType, 23 | HttpResponseMessage httpResponseMessage 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Repository/UserRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Domain.AggregateRoots; 5 | using Domain.IRepositories; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | namespace Repository 9 | { 10 | public class UserRepository : IUserRepository 11 | { 12 | private readonly CustomDbContext _dbContext; 13 | 14 | public UserRepository(CustomDbContext customDbContext) 15 | { 16 | _dbContext = customDbContext; 17 | } 18 | 19 | public async Task AddAsync(User user) 20 | { 21 | await _dbContext.Users.AddAsync(user); 22 | } 23 | 24 | public async Task GetAsync(Guid id) 25 | { 26 | return await _dbContext.Users.FindAsync(id); 27 | } 28 | 29 | public async Task> GetAllAsync() 30 | { 31 | return await _dbContext.Users.ToListAsync(); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Aoxe.Shared/LoadHelper.LoadTypes.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Shared; 2 | 3 | public static partial class LoadHelper 4 | { 5 | private static readonly List SpecifyTypes = new(); 6 | 7 | public static LoadTypesMode LoadMode { get; set; } = LoadTypesMode.LoadByAllDirectory; 8 | 9 | public static IReadOnlyList LoadAllDirectoryTypes() => DirectoryTypesLazy.Value; 10 | 11 | public static IReadOnlyList LoadSpecifyTypes() => SpecifyTypes; 12 | 13 | public static IReadOnlyList LoadTypes() => 14 | LoadMode switch 15 | { 16 | LoadTypesMode.LoadBySpecify => LoadSpecifyTypes(), 17 | _ => LoadAllDirectoryTypes() 18 | }; 19 | 20 | private static readonly Lazy> DirectoryTypesLazy = 21 | new(() => 22 | { 23 | var result = LoadFromDirectories(Directory.GetCurrentDirectory()); 24 | LoadMode = LoadTypesMode.LoadByAllDirectory; 25 | return result; 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/ServiceHost/ServiceHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/DomainEvents/UserTagsSetEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Domain.AggregateRoots; 5 | using Aoxe.SequentialGuid; 6 | using Aoxe.DDD.Abstractions.Domain; 7 | 8 | namespace Domain.DomainEvents 9 | { 10 | public record UserTagsSetEvent : IDomainEvent 11 | { 12 | public Guid Id { get; private set; } 13 | public Guid EntityId { get; private set; } 14 | public int EntityVersion { get; } 15 | public DateTime EventTime { get; private set; } 16 | public List Tags { get; private set; } 17 | 18 | public UserTagsSetEvent(User user) 19 | { 20 | if (user is null) throw new ArgumentNullException(nameof(user)); 21 | Id = SequentialGuidHelper.GenerateComb(); 22 | EntityId = user.Id; 23 | EntityVersion = user.Version; 24 | Tags = user.Tags.ToList(); 25 | EventTime = DateTime.UtcNow; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET 2 | # Build and test ASP.NET projects. 3 | # Add steps that publish symbols, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - task: NuGetToolInstaller@0 19 | 20 | - task: NuGetCommand@2 21 | inputs: 22 | restoreSolution: '$(solution)' 23 | 24 | - task: VSBuild@1 25 | inputs: 26 | solution: '$(solution)' 27 | msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"' 28 | platform: '$(buildPlatform)' 29 | configuration: '$(buildConfiguration)' 30 | 31 | - task: VSTest@2 32 | inputs: 33 | platform: '$(buildPlatform)' 34 | configuration: '$(buildConfiguration)' 35 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD/Aoxe.IServiceCollection.Extensions.Domain.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddDomainService(this IServiceCollection services) => 6 | services 7 | .Register(ServiceLifetime.Scoped) 8 | .Register(ServiceLifetime.Scoped); 9 | 10 | public static IServiceCollection AddDomainFactory(this IServiceCollection services) => 11 | services 12 | .Register(ServiceLifetime.Scoped) 13 | .Register(ServiceLifetime.Scoped); 14 | 15 | public static IServiceCollection AddDomainEventPublisher(this IServiceCollection services) => 16 | services.AddHostedService(); 17 | 18 | public static IServiceCollection AddDomainEventHandler(this IServiceCollection services) => 19 | services 20 | .Register(ServiceLifetime.Scoped) 21 | .Register(ServiceLifetime.Scoped); 22 | } 23 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/Jil.Formatter.Test.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Tests; 2 | 3 | public partial class FormatterTest 4 | { 5 | [Fact] 6 | public async Task JilFormatterTest() 7 | { 8 | var serializer = new Aoxe.Jil.Serializer( 9 | new Options( 10 | dateFormat: DateTimeFormat.ISO8601, 11 | excludeNulls: true, 12 | includeInherited: true, 13 | serializationNameFormat: SerializationNameFormat.CamelCase 14 | ) 15 | ); 16 | await TextFormatterAsync(serializer, "application/x-jil"); 17 | } 18 | 19 | [Fact] 20 | public async Task JilFormatterNullTest() 21 | { 22 | var serializer = new Aoxe.Jil.Serializer( 23 | new Options( 24 | dateFormat: DateTimeFormat.ISO8601, 25 | excludeNulls: true, 26 | includeInherited: true, 27 | serializationNameFormat: SerializationNameFormat.CamelCase 28 | ) 29 | ); 30 | await TextFormatterNullAsync(serializer, "application/x-jil"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Demo; 2 | 3 | public class Startup 4 | { 5 | public Startup(IConfiguration configuration) 6 | { 7 | Configuration = configuration; 8 | } 9 | 10 | private IConfiguration Configuration { get; } 11 | 12 | // This method gets called by the runtime. Use this method to add services to the container. 13 | public void ConfigureServices(IServiceCollection services) 14 | { 15 | services 16 | .AddControllers() 17 | .AddNewtonsoftJson() 18 | .AddMsgPack() 19 | .AddProtobuf() 20 | .AddUtf8Json() 21 | .AddZeroFormatter(); 22 | } 23 | 24 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 25 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 26 | { 27 | if (env.IsDevelopment()) 28 | { 29 | app.UseDeveloperExceptionPage(); 30 | } 31 | 32 | app.UseRouting(); 33 | app.UseEndpoints(endpoints => endpoints.MapControllers()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/ServiceCollectionExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Npgsql; 6 | using QueryService; 7 | 8 | namespace BackendForBrowser 9 | { 10 | public static class ServiceCollectionExtension 11 | { 12 | public static IServiceCollection AddQueryService(this IServiceCollection services) 13 | { 14 | services.Scan(scan => scan.FromAssemblyOf() 15 | .AddClasses(classes => 16 | classes.Where(@class => @class.Name.EndsWith("QueryService", StringComparison.OrdinalIgnoreCase))) 17 | .AsSelf().WithScopedLifetime()); 18 | return services; 19 | } 20 | 21 | public static IServiceCollection AddDbConnection(this IServiceCollection services, IConfiguration config) 22 | { 23 | services.AddScoped( 24 | _ => new NpgsqlConnection(config.GetSection("PgSqlStandby").Get())); 25 | return services; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.MsgPack/MvcOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.MsgPack; 2 | 3 | public static class MvcOptionsExtension 4 | { 5 | public static void AddMsgPackFormatter( 6 | this MvcOptions options, 7 | string contentType = "application/x-msgpack", 8 | string format = "msgpack" 9 | ) 10 | { 11 | if (string.IsNullOrWhiteSpace(contentType)) 12 | throw new ArgumentNullException(nameof(contentType)); 13 | if (string.IsNullOrWhiteSpace(format)) 14 | throw new ArgumentNullException(nameof(format)); 15 | 16 | var mediaTypeHeaderValue = MediaTypeHeaderValue 17 | .Parse((StringSegment)contentType) 18 | .CopyAsReadOnly(); 19 | var serializer = new Aoxe.MsgPack.Serializer(); 20 | options.InputFormatters.Add(new AoxeInputFormatter(mediaTypeHeaderValue, serializer)); 21 | options.OutputFormatters.Add(new AoxeOutputFormatter(mediaTypeHeaderValue, serializer)); 22 | options.FormatterMappings.SetMediaTypeMappingForFormat(format, mediaTypeHeaderValue); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Protobuf/MvcOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Protobuf; 2 | 3 | public static class MvcOptionsExtension 4 | { 5 | public static void AddProtobufFormatter( 6 | this MvcOptions options, 7 | string contentType = "application/x-protobuf", 8 | string format = "protobuf" 9 | ) 10 | { 11 | if (string.IsNullOrWhiteSpace(contentType)) 12 | throw new ArgumentNullException(nameof(contentType)); 13 | if (string.IsNullOrWhiteSpace(format)) 14 | throw new ArgumentNullException(nameof(format)); 15 | 16 | var mediaTypeHeaderValue = MediaTypeHeaderValue 17 | .Parse((StringSegment)contentType) 18 | .CopyAsReadOnly(); 19 | var serializer = new Aoxe.Protobuf.Serializer(); 20 | options.InputFormatters.Add(new AoxeInputFormatter(mediaTypeHeaderValue, serializer)); 21 | options.OutputFormatters.Add(new AoxeOutputFormatter(mediaTypeHeaderValue, serializer)); 22 | options.FormatterMappings.SetMediaTypeMappingForFormat(format, mediaTypeHeaderValue); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/DomainEvents/UserCardAddedEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Domain.AggregateRoots; 3 | using Domain.Entities; 4 | using Aoxe.SequentialGuid; 5 | using Aoxe.DDD.Abstractions.Domain; 6 | 7 | namespace Domain.DomainEvents 8 | { 9 | public record UserCardAddedEvent : IDomainEvent 10 | { 11 | public Guid Id { get; private set; } 12 | public Guid EntityId { get; private set; } 13 | public int EntityVersion { get; } 14 | public DateTime EventTime { get; private set; } 15 | public Guid CardId { get; private set; } 16 | public string CardName { get; private set; } 17 | 18 | public UserCardAddedEvent(User user, Card card) 19 | { 20 | if (user is null) throw new ArgumentNullException(nameof(user)); 21 | if (card is null) throw new ArgumentNullException(nameof(card)); 22 | Id = SequentialGuidHelper.GenerateComb(); 23 | EntityId = user.Id; 24 | EntityVersion = user.Version; 25 | CardId = card.Id; 26 | CardName = card.Name; 27 | EventTime = DateTime.UtcNow; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2022 XiaoFei Du 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.ZeroFormatter/MvcOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.ZeroFormatter; 2 | 3 | public static class MvcOptionsExtension 4 | { 5 | public static void AddZeroFormatter( 6 | this MvcOptions options, 7 | string contentType = "application/x-zeroformatter", 8 | string format = "zeroformatter" 9 | ) 10 | { 11 | if (string.IsNullOrWhiteSpace(contentType)) 12 | throw new ArgumentNullException(nameof(contentType)); 13 | if (string.IsNullOrWhiteSpace(format)) 14 | throw new ArgumentNullException(nameof(format)); 15 | 16 | var mediaTypeHeaderValue = MediaTypeHeaderValue 17 | .Parse((StringSegment)contentType) 18 | .CopyAsReadOnly(); 19 | var serializer = new Aoxe.ZeroFormatter.Serializer(); 20 | options.InputFormatters.Add(new AoxeInputFormatter(mediaTypeHeaderValue, serializer)); 21 | options.OutputFormatters.Add(new AoxeOutputFormatter(mediaTypeHeaderValue, serializer)); 22 | options.FormatterMappings.SetMediaTypeMappingForFormat(format, mediaTypeHeaderValue); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Aoxe/Aoxe.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | Library 5 | true 6 | 2022.4.0 7 | 2022.4.0 8 | Mutuduxf 9 | Mutuduxf 10 | Aoxe MicroService Framework 11 | A micro service framework base by asp.net core 12 | https://github.com/AoxeTech/Aoxe 13 | MIT 14 | enable 15 | latest 16 | enable 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Jil/MvcOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Jil; 2 | 3 | public static class MvcOptionsExtension 4 | { 5 | public static void AddJilFormatter( 6 | this MvcOptions options, 7 | string contentType = "application/x-jil", 8 | string format = "jil", 9 | Options? jilOptions = null 10 | ) 11 | { 12 | if (string.IsNullOrWhiteSpace(contentType)) 13 | throw new ArgumentNullException(nameof(contentType)); 14 | if (string.IsNullOrWhiteSpace(format)) 15 | throw new ArgumentNullException(nameof(format)); 16 | 17 | var mediaTypeHeaderValue = MediaTypeHeaderValue 18 | .Parse((StringSegment)contentType) 19 | .CopyAsReadOnly(); 20 | var serializer = new Aoxe.Jil.Serializer(jilOptions); 21 | options.InputFormatters.Add(new AoxeTextInputFormatter(mediaTypeHeaderValue, serializer)); 22 | options.OutputFormatters.Add(new AoxeTextOutputFormatter(mediaTypeHeaderValue, serializer)); 23 | options.FormatterMappings.SetMediaTypeMappingForFormat(format, mediaTypeHeaderValue); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/Aoxe.AspNetCore.Formatters.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | latest 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | all 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/ConsoleHost/Program.cs: -------------------------------------------------------------------------------- 1 | using Aoxe; 2 | using Aoxe.Server; 3 | using Aoxe.ThreeTier.Abstractions.BusinessLogic; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace ConsoleHost 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | AoxeHost 14 | .Instance 15 | .AddAoxeService() 16 | .AddThreeTiers() 17 | .ConfigureServices(services => 18 | { 19 | services.AddSwaggerDocument(); 20 | }) 21 | .Configure(app => 22 | { 23 | app.UseHttpsRedirection() 24 | .UseOpenApi() 25 | .UseSwaggerUi() 26 | .UseAoxe() 27 | .UseRouting() 28 | .UseAuthorization() 29 | .UseEndpoints(endpoints => 30 | { 31 | endpoints.MapControllers(); 32 | }); 33 | }) 34 | .Run(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/ServiceHost/DomainEventBackgroundService.cs: -------------------------------------------------------------------------------- 1 | namespace ServiceHost; 2 | 3 | public class DomainEventBackgroundService : BackgroundService 4 | { 5 | private readonly IAoxeRabbitMqClient _rabbitMqClient; 6 | 7 | public DomainEventBackgroundService(IAoxeRabbitMqClient rabbitMqClient) 8 | { 9 | _rabbitMqClient = rabbitMqClient; 10 | } 11 | 12 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 13 | { 14 | // await _rabbitMqClient.SubscribeEventAsync( 15 | // "Domain.DomainEvents.UserBirthdayCelebratedEvent", null); 16 | // await _rabbitMqClient.SubscribeEventAsync( 17 | // "Domain.DomainEvents.UserCardAddedEvent", null); 18 | // await _rabbitMqClient.SubscribeEventAsync( 19 | // "Domain.DomainEvents.UserCreatedEvent", null); 20 | // await _rabbitMqClient.SubscribeEventAsync( 21 | // "Domain.DomainEvents.UserNameChangedEvent", null); 22 | // await _rabbitMqClient.SubscribeEventAsync( 23 | // "Domain.DomainEvents.UserTagsSetEvent", null); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Utf8Json/MvcOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Utf8Json; 2 | 3 | public static class MvcOptionsExtension 4 | { 5 | public static void AddUtf8JsonFormatter( 6 | this MvcOptions options, 7 | string contentType = "application/x-utf8json", 8 | string format = "utf8json", 9 | IJsonFormatterResolver? resolver = null 10 | ) 11 | { 12 | if (string.IsNullOrWhiteSpace(contentType)) 13 | throw new ArgumentNullException(nameof(contentType)); 14 | if (string.IsNullOrWhiteSpace(format)) 15 | throw new ArgumentNullException(nameof(format)); 16 | 17 | var mediaTypeHeaderValue = MediaTypeHeaderValue 18 | .Parse((StringSegment)contentType) 19 | .CopyAsReadOnly(); 20 | var serializer = new Aoxe.Utf8Json.Serializer(resolver); 21 | options.InputFormatters.Add(new AoxeTextInputFormatter(mediaTypeHeaderValue, serializer)); 22 | options.OutputFormatters.Add(new AoxeTextOutputFormatter(mediaTypeHeaderValue, serializer)); 23 | options.FormatterMappings.SetMediaTypeMappingForFormat(format, mediaTypeHeaderValue); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Aoxe.Shared/Aoxe.IServiceCollection.Extensions.Register.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Shared; 2 | 3 | public static partial class AoxeIServiceCollectionExtensions 4 | { 5 | public static IServiceCollection Register( 6 | this IServiceCollection services, 7 | ServiceLifetime serviceLifetime 8 | ) => services.Register(typeof(TDefineType), serviceLifetime); 9 | 10 | public static IServiceCollection Register( 11 | this IServiceCollection services, 12 | Type defineType, 13 | ServiceLifetime serviceLifetime 14 | ) 15 | { 16 | var typePairs = LoadHelper.GetTypePairs(defineType); 17 | return services.Register(typePairs, serviceLifetime); 18 | } 19 | 20 | private static IServiceCollection Register( 21 | this IServiceCollection services, 22 | IEnumerable typePairs, 23 | ServiceLifetime serviceLifetime 24 | ) 25 | { 26 | var serviceDescriptors = typePairs.Select( 27 | tp => new ServiceDescriptor(tp.InterfaceType, tp.ImplementationType, serviceLifetime) 28 | ); 29 | foreach (var serviceDescriptor in serviceDescriptors) 30 | services.Add(serviceDescriptor); 31 | return services; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /demo/Aoxe.AspNetCore.Formatters.Demo/Aoxe.AspNetCore.Formatters.Demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Client/Http/Aoxe.Client.Http/Internal/TaskCompletionSource.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Internal; 2 | 3 | internal class TaskCompletionSource 4 | { 5 | private readonly ITaskCompletionSource _taskSource; 6 | 7 | public Task Task => _taskSource.Task; 8 | 9 | public TaskCompletionSource(Type resultType) 10 | { 11 | var type = typeof(TaskCompletionSourceOf<>).MakeGenericType(resultType); 12 | _taskSource = (Activator.CreateInstance(type) as ITaskCompletionSource)!; 13 | } 14 | 15 | public bool SetResult(object? result) => _taskSource.SetResult(result); 16 | 17 | public bool SetException(Exception ex) => _taskSource.SetException(ex); 18 | 19 | private interface ITaskCompletionSource 20 | { 21 | Task Task { get; } 22 | 23 | bool SetResult(object? result); 24 | 25 | bool SetException(Exception ex); 26 | } 27 | 28 | private class TaskCompletionSourceOf : ITaskCompletionSource 29 | { 30 | private readonly TaskCompletionSource _taskSource = new(); 31 | 32 | public Task Task => _taskSource.Task; 33 | 34 | public bool SetResult(object? result) => _taskSource.TrySetResult((TResult?)result); 35 | 36 | public bool SetException(Exception ex) => _taskSource.TrySetException(ex); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters/AoxeInputFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters; 2 | 3 | public class AoxeInputFormatter : InputFormatter 4 | { 5 | private readonly IBytesSerializer _bytesSerializer; 6 | private readonly IStreamSerializerAsync? _streamSerializerAsync; 7 | 8 | public AoxeInputFormatter(MediaTypeHeaderValue contentType, IBytesSerializer bytesSerializer) 9 | { 10 | SupportedMediaTypes.Add(contentType); 11 | _bytesSerializer = bytesSerializer; 12 | _streamSerializerAsync = bytesSerializer as IStreamSerializerAsync; 13 | } 14 | 15 | public override async Task ReadRequestBodyAsync( 16 | InputFormatterContext context 17 | ) => 18 | _streamSerializerAsync is null 19 | ? await InputFormatterResult.SuccessAsync( 20 | _bytesSerializer.FromBytes( 21 | context.ModelType, 22 | await context.HttpContext.Request.Body.ReadToEndAsync() 23 | )! 24 | ) 25 | : await InputFormatterResult.SuccessAsync( 26 | ( 27 | await _streamSerializerAsync.FromStreamAsync( 28 | context.ModelType, 29 | context.HttpContext.Request.Body 30 | ) 31 | )! 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/AspNetCoreFormatterTest.Text.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Tests; 2 | 3 | public partial class FormatterTest 4 | { 5 | private async Task TextFormatterAsync(ITextSerializer textSerializer, string mediaType) 6 | { 7 | var dtos = GetDtos(); 8 | 9 | var httpRequestMessage = CreateHttpRequestMessage( 10 | new StringContent(textSerializer.ToText(dtos), Encoding.UTF8, mediaType), 11 | mediaType 12 | ); 13 | 14 | var response = await _server.CreateClient().SendAsync(httpRequestMessage); 15 | var result = textSerializer.FromText>( 16 | await response.Content.ReadAsStringAsync() 17 | ); 18 | 19 | Assert.True(CompareDtos(dtos, result)); 20 | } 21 | 22 | private async Task TextFormatterNullAsync(ITextSerializer textSerializer, string mediaType) 23 | { 24 | var httpRequestMessage = CreateHttpRequestMessage( 25 | new StringContent(textSerializer.ToText>(null), Encoding.UTF8, mediaType), 26 | mediaType 27 | ); 28 | 29 | var response = await _server.CreateClient().SendAsync(httpRequestMessage); 30 | var result = textSerializer.FromText>( 31 | await response.Content.ReadAsStringAsync() 32 | ); 33 | 34 | Assert.Null(result); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Aoxe.AspNetCore.Formatters.Tests/AspNetCoreFormatterTest.Stream.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters.Tests; 2 | 3 | public partial class FormatterTest 4 | { 5 | private async Task StreamFormatterAsync(IStreamSerializer streamSerializer, string mediaType) 6 | { 7 | var dtos = GetDtos(); 8 | 9 | var httpRequestMessage = CreateHttpRequestMessage( 10 | new StreamContent(streamSerializer.ToStream(dtos)), 11 | mediaType 12 | ); 13 | 14 | var response = await _server.CreateClient().SendAsync(httpRequestMessage); 15 | var result = streamSerializer.FromStream>( 16 | await response.Content.ReadAsStreamAsync() 17 | ); 18 | 19 | Assert.True(CompareDtos(dtos, result)); 20 | } 21 | 22 | private async Task StreamFormatterNullAsync( 23 | IStreamSerializer streamSerializer, 24 | string mediaType 25 | ) 26 | { 27 | var httpRequestMessage = CreateHttpRequestMessage( 28 | new StreamContent(streamSerializer.ToStream>(null)), 29 | mediaType 30 | ); 31 | 32 | var response = await _server.CreateClient().SendAsync(httpRequestMessage); 33 | var result = streamSerializer.FromStream>( 34 | await response.Content.ReadAsStreamAsync() 35 | ); 36 | 37 | Assert.Null(result); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/DataAccessLayer/UserDal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using Aoxe.ThreeTier.Abstractions.DataAccess; 5 | using Dapper; 6 | using Model; 7 | using Zaabee.Dapper.Extensions; 8 | 9 | namespace DataAccessLayer 10 | { 11 | public class UserDal : IDal 12 | { 13 | private readonly IDbConnection _connection; 14 | 15 | public UserDal(IDbConnection connection) 16 | { 17 | _connection = connection; 18 | } 19 | 20 | public int Add(User user) 21 | { 22 | return _connection.Add(user); 23 | } 24 | 25 | public int Update(User user) 26 | { 27 | return _connection.Update(user); 28 | } 29 | 30 | public int Delete(User user) 31 | { 32 | return _connection.DeleteByEntity(user); 33 | } 34 | 35 | public User GetById(Guid id) 36 | { 37 | return _connection.FirstOrDefault(id); 38 | } 39 | 40 | public User GetByName(string name) 41 | { 42 | return _connection.QueryFirstOrDefault( 43 | "SELECT * FROM \"User\" WHERE \"Name\" = @Name", 44 | new { Name = name } 45 | ); 46 | } 47 | 48 | public IList GetAll() 49 | { 50 | return _connection.GetAll(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/BusinessLogicLayer/UserBll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Aoxe.SequentialGuid; 4 | using Aoxe.ThreeTier.Abstractions.BusinessLogic; 5 | using BusinessLogicLayer.Dtos; 6 | using DataAccessLayer; 7 | using Model; 8 | 9 | namespace BusinessLogicLayer 10 | { 11 | public class UserBll : IBll 12 | { 13 | private readonly UserDal _userDal; 14 | 15 | public UserBll(UserDal userDal) 16 | { 17 | _userDal = userDal; 18 | } 19 | 20 | public bool Login(LoginRequestParam param) 21 | { 22 | var user = _userDal.GetByName(param.Name); 23 | if (user is null) 24 | throw new ApplicationException("The user is not exist."); 25 | return user.Pwd == param.Pwd; 26 | } 27 | 28 | public bool CreateUser(CreateUserDto dto) 29 | { 30 | var user = new User 31 | { 32 | Id = SequentialGuidHelper.GenerateComb(), 33 | Name = dto.Name, 34 | Pwd = dto.Pwd, 35 | Nickname = dto.Nickname, 36 | CreatedUtcTime = DateTime.UtcNow 37 | }; 38 | return _userDal.Add(user) > 0; 39 | } 40 | 41 | public User GetById(Guid id) 42 | { 43 | return _userDal.GetById(id); 44 | } 45 | 46 | public IList GetAll() 47 | { 48 | return _userDal.GetAll(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demo/Aoxe.ThreeTier/WebApiHost/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace WebApiHost; 2 | 3 | public class Startup 4 | { 5 | public Startup(IConfiguration configuration) 6 | { 7 | Configuration = configuration; 8 | } 9 | 10 | public IConfiguration Configuration { get; } 11 | 12 | // This method gets called by the runtime. Use this method to add services to the container. 13 | public void ConfigureServices(IServiceCollection services) 14 | { 15 | services.AddControllers(); 16 | services 17 | .AddAoxeService() 18 | .AddThreeTier() 19 | .AddDbConnection(Configuration.GetConnectionString("PgSql")); 20 | services.AddSwaggerGen(c => 21 | { 22 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApiHost", Version = "v1" }); 23 | }); 24 | } 25 | 26 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 27 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 28 | { 29 | if (env.IsDevelopment()) 30 | { 31 | app.UseDeveloperExceptionPage(); 32 | app.UseSwagger(); 33 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApiHost v1")); 34 | } 35 | 36 | app.UseHttpsRedirection(); 37 | 38 | app.UseRouting(); 39 | 40 | app.UseAuthorization(); 41 | 42 | app.UseEndpoints(endpoints => 43 | { 44 | endpoints.MapControllers(); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /demo/Aoxe/BobHost/BobHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi/AoxeActionModelConvention.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.WebApi; 2 | 3 | internal class AoxeActionModelConvention : IActionModelConvention 4 | { 5 | private readonly Type _serviceType; 6 | 7 | public AoxeActionModelConvention(Type serviceType) => _serviceType = serviceType; 8 | 9 | public void Apply(ActionModel action) 10 | { 11 | if (!_serviceType.IsAssignableFrom(action.Controller.ControllerType)) 12 | return; 13 | 14 | action.Selectors.Clear(); 15 | var route = 16 | _serviceType.GetCustomAttribute(typeof(RouteAttribute), false) as RouteAttribute; 17 | var template = $"{_serviceType.FullName?.Replace('.', '/')}/[action]"; 18 | action.Selectors.Add( 19 | CreateSelector(route ?? new RouteAttribute(template) { Name = template }) 20 | ); 21 | 22 | foreach (var parameter in action.Parameters) 23 | { 24 | parameter.BindingInfo ??= new BindingInfo(); 25 | parameter.BindingInfo.BindingSource = BindingSource.Body; 26 | } 27 | } 28 | 29 | private static SelectorModel CreateSelector(IRouteTemplateProvider routeTemplateProvider) 30 | { 31 | var selectorModel = new SelectorModel 32 | { 33 | AttributeRouteModel = new AttributeRouteModel(routeTemplateProvider) 34 | }; 35 | 36 | selectorModel.ActionConstraints.Add( 37 | new HttpMethodActionConstraint(new List { "POST" }) 38 | ); 39 | 40 | return selectorModel; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Aoxe.Shared.Test/Aoxe.Shared.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | runtime; build; native; contentfiles; analyzers; buildtransitive 11 | all 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Aoxe.Shared/LoadHelper.LoadByDirectory.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Shared; 2 | 3 | public static partial class LoadHelper 4 | { 5 | public static void FromDirectories(params string[] directories) 6 | { 7 | SpecifyTypes.AddRange(LoadFromDirectories(directories)); 8 | LoadMode = LoadTypesMode.LoadBySpecify; 9 | } 10 | 11 | private static List LoadFromDirectories(params string[] directories) 12 | { 13 | var files = directories.SelectMany(dir => 14 | Directory 15 | .GetFiles(dir + @"/", "*.dll", SearchOption.AllDirectories) 16 | .Union(Directory.GetFiles(dir + @"/", "*.exe", SearchOption.AllDirectories)) 17 | ); 18 | 19 | var result = files 20 | .Select(file => 21 | { 22 | try 23 | { 24 | return Assembly.LoadFrom(file).ExportedTypes; 25 | } 26 | catch (BadImageFormatException) 27 | { 28 | return null; 29 | } 30 | catch (FileLoadException) 31 | { 32 | return null; 33 | } 34 | catch (Exception) 35 | { 36 | return null; 37 | } 38 | }) 39 | .Where(types => types is not null) 40 | .SelectMany(types => types!) 41 | .Distinct() 42 | .ToList(); 43 | 44 | LoadMode = LoadTypesMode.LoadByAllDirectory; 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters/AoxeTextInputFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.AspNetCore.Formatters; 2 | 3 | public class AoxeTextInputFormatter : TextInputFormatter 4 | { 5 | private readonly ITextSerializer _textSerializer; 6 | private readonly IStreamSerializerAsync? _streamSerializerAsync; 7 | 8 | public AoxeTextInputFormatter( 9 | MediaTypeHeaderValue mediaTypeHeaderValue, 10 | ITextSerializer textSerializer 11 | ) 12 | { 13 | SupportedEncodings.Add(UTF8EncodingWithoutBOM); 14 | SupportedEncodings.Add(UTF16EncodingLittleEndian); 15 | SupportedMediaTypes.Add(mediaTypeHeaderValue); 16 | _textSerializer = textSerializer; 17 | _streamSerializerAsync = textSerializer as IStreamSerializerAsync; 18 | } 19 | 20 | public override async Task ReadRequestBodyAsync( 21 | InputFormatterContext context, 22 | Encoding encoding 23 | ) => 24 | _streamSerializerAsync is null 25 | ? await InputFormatterResult.SuccessAsync( 26 | _textSerializer.FromBytes( 27 | context.ModelType, 28 | await context.HttpContext.Request.Body.ReadToEndAsync() 29 | )! 30 | ) 31 | : await InputFormatterResult.SuccessAsync( 32 | ( 33 | await _streamSerializerAsync.FromStreamAsync( 34 | context.ModelType, 35 | context.HttpContext.Request.Body 36 | ) 37 | )! 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /demo/Aoxe/CarolHost/CarolHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Protobuf/Aoxe.Client.Http.Protobuf.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | The protobuf formatter for Aoxe http client. 8 | Aoxe;MicroService;Http Client;Formatter;Protobuf; 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Aoxe.Core/Aoxe.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.2.0 6 | 2024.2.0 7 | The core library for Aoxe. 8 | Aoxe;MicroService;Framework 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | all 29 | runtime; build; native; contentfiles; analyzers; buildtransitive 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Formatter/AoxeHttpClientStreamFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Formatter; 2 | 3 | public class AoxeHttpClientStreamFormatter : AoxeHttpClientFormatter 4 | { 5 | private readonly IStreamSerializer _serializer; 6 | public string MediaType { get; } 7 | 8 | public AoxeHttpClientStreamFormatter(AoxeClientFormatterOptions options) 9 | { 10 | _serializer = options.Serializer; 11 | MediaType = options.MediaType; 12 | } 13 | 14 | public override HttpRequestMessage CreateHttpRequestMessage(string requestUri, object? message) 15 | { 16 | var httpContent = new StreamContent(_serializer.ToStream(message)); 17 | return CreateHttpRequestMessage(httpContent, MediaType, requestUri); 18 | } 19 | 20 | public override async Task GetResultAsync( 21 | Type returnType, 22 | HttpResponseMessage httpResponseMessage 23 | ) 24 | { 25 | var result = await httpResponseMessage.Content.ReadAsStreamAsync(); 26 | var type = 27 | returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>) 28 | ? returnType.GenericTypeArguments[0] 29 | : returnType; 30 | if (httpResponseMessage.IsSuccessStatusCode) 31 | return _serializer.FromStream(type, result); 32 | 33 | var aoxeError = _serializer.FromStream(result)!; 34 | throw new AoxeException(aoxeError.Message) 35 | { 36 | Id = aoxeError.Id, 37 | Code = aoxeError.Code, 38 | ThrowTime = aoxeError.ThrowTime, 39 | Source = aoxeError.Source 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Aoxe.Shared/Aoxe.Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.3.0 6 | 2024.3.0 7 | Common classes and tools for Aoxe. 8 | Aoxe;MicroService;Framework 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | all 29 | runtime; build; native; contentfiles; analyzers; buildtransitive 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier.Abstractions/Aoxe.ThreeTier.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.2.0 6 | 2024.2.0 7 | Abstractions for Aoxe.ThreeTier 8 | Aoxe;ThreeTier 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | all 29 | runtime; build; native; contentfiles; analyzers; buildtransitive 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /demo/Aoxe/AliceHost/AliceHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Architecture/ThreeTier/Aoxe.ThreeTier/Aoxe.ThreeTier.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | Three tier framework for Aoxe. 8 | Aoxe;ThreeTier 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/README.md: -------------------------------------------------------------------------------- 1 | # Aoxe.AspNetCore.Formatters 2 | 3 | Formatters for asp.net core 4 | 5 | [JilFormatters](https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters.Jil) 6 | 7 | [MsgPackFormatters](https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters.MsgPack) 8 | 9 | [ProtobufFormatters](https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters.Protobuf) 10 | 11 | [Utf8JsonFormatters](https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters.Utf8Json) 12 | 13 | [ZeroFormatters](https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters.ZeroFormatter) 14 | 15 | ## Benchmark 16 | 17 | ``` ini 18 | BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1466 (21H1/May2021Update) 19 | Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores 20 | .NET SDK=6.0.101 21 | [Host] : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT 22 | DefaultJob : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT 23 | ``` 24 | 25 | | Method | Mean | Error | StdDev | Min | Max | Median | 26 | |------------- |---------:|--------:|--------:|---------:|---------:|---------:| 27 | | JilPost | 148.9 μs | 2.78 μs | 2.73 μs | 143.0 μs | 153.5 μs | 148.4 μs | 28 | | ProtobufPost | 146.0 μs | 2.83 μs | 4.32 μs | 125.7 μs | 152.1 μs | 146.2 μs | 29 | | JsonPost | 2249 μs | 152.8 μs | 445.8 μs | 2076 μs | 1622 μs | 3417 μs | 30 | | MsgPackPost | 201.7 μs | 3.87 μs | 7.90 μs | 198.3 μs | 192.9 μs | 225.3 μs | 31 | | Utf8JsonPost | 155.9 μs | 2.23 μs | 1.98 μs | 153.8 μs | 159.8 μs | 155.5 μs | 32 | | ZeroFormatterPost | 127.8 μs | 2.46 μs | 3.20 μs | 119.2 μs | 132.7 μs | 127.6 μs | 33 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD.Abstractions/Aoxe.DDD.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0;net8.0 4 | 2024.2.0 5 | 2024.2.0 6 | Abstractions for Aoxe.DDD 7 | Aoxe DDD 8 | https://github.com/AoxeTech/Aoxe 9 | https://github.com/AoxeTech/Aoxe 10 | MIT 11 | latest 12 | enable 13 | enable 14 | git 15 | true 16 | snupkg 17 | true 18 | true 19 | true 20 | true 21 | Mutuduxf 22 | 23 | 24 | 25 | 26 | all 27 | runtime; build; native; contentfiles; analyzers; buildtransitive 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Protobuf/Aoxe.AspNetCore.Formatters.Protobuf.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | Library 8 | true 9 | Protobuf formatters for asp.net core 10 | Aoxe;asp.net core formatters;protobuf 11 | https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters 12 | https://github.com/AoxeTech/Aoxe 13 | MIT 14 | latest 15 | enable 16 | enable 17 | git 18 | true 19 | snupkg 20 | true 21 | true 22 | true 23 | true 24 | Mutuduxf 25 | 26 | 27 | 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Jil/Aoxe.Client.Http.Jil.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | The jil formatter for Aoxe http client. 8 | Aoxe;MicroService;Http Client;Formatter;Jil; 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Formatter/Aoxe.Client.Http.Formatter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | The formatter for Aoxe http client. 8 | Aoxe;MicroService;Http Client;Formatter; 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/MicroService/Domain/DomainServices/UserDomainService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Aoxe.DDD.Abstractions.Domain; 5 | using Domain.AggregateRoots; 6 | using Domain.Entities; 7 | using Domain.IRepositories; 8 | 9 | namespace Domain.DomainServices 10 | { 11 | public class UserDomainService : IDomainService 12 | { 13 | private readonly IUserRepository _userRepository; 14 | 15 | public UserDomainService(IUserRepository userRepository) 16 | { 17 | _userRepository = userRepository; 18 | } 19 | 20 | public async Task> GetAllUsersAsync() => await _userRepository.GetAllAsync(); 21 | 22 | public async Task GetUserAsync(Guid userId) => await _userRepository.GetAsync(userId); 23 | 24 | public async Task AddUser(User user) 25 | { 26 | await _userRepository.AddAsync(user); 27 | } 28 | 29 | public async Task ChangeNameAsync(Guid userId, string name) 30 | { 31 | var user = await _userRepository.GetAsync(userId); 32 | user?.ChangeName(name); 33 | } 34 | 35 | public async Task CelebrateBirthdayAsync(Guid userId) 36 | { 37 | var user = await _userRepository.GetAsync(userId); 38 | user?.CelebrateBirthday(); 39 | } 40 | 41 | public async Task SetTagsAsync(Guid userId, params string[] tags) 42 | { 43 | var user = await _userRepository.GetAsync(userId); 44 | user?.SetTags(tags); 45 | } 46 | 47 | public async Task AddCardAsync(Guid userId, Card card) 48 | { 49 | var user = await _userRepository.GetAsync(userId); 50 | user?.AddCard(card); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.MsgPack/Aoxe.Client.Http.MsgPack.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | The MsgPack formatter for Aoxe http client. 8 | Aoxe;MicroService;Http Client;Formatter;MsgPack; 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Utf8Json/Aoxe.Client.Http.Utf8Json.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | The utf8json formatter for Aoxe http client. 8 | Aoxe;MicroService;Http Client;Formatter;Utf8Json; 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi/Aoxe.WebApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | Library 8 | true 9 | A micro service framework base by asp.net core 10 | Aoxe;MicroService;Framework 11 | https://github.com/AoxeTech/Aoxe 12 | https://github.com/AoxeTech/Aoxe 13 | MIT 14 | latest 15 | enable 16 | enable 17 | git 18 | true 19 | snupkg 20 | true 21 | true 22 | true 23 | true 24 | Mutuduxf 25 | 26 | 27 | 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /demo/Aoxe/BobHost/Program.cs: -------------------------------------------------------------------------------- 1 | namespace BobHost; 2 | 3 | class Program 4 | { 5 | public static void Main(string[] args) 6 | { 7 | AoxeHost 8 | .Instance.FromAssemblyOf() 9 | .FromAssemblyOf(typeof(IBobService)) 10 | .FromAssemblies(typeof(ICarolService).Assembly) 11 | .FromAssemblyNames(typeof(BobService).Assembly.GetName()) 12 | .AddAoxeService() 13 | .UseAoxeClient( 14 | typeof(IService), 15 | new Dictionary 16 | { 17 | { "IAliceServices", "http://localhost:5001" }, 18 | { "ICarolServices", "http://localhost:5003" } 19 | }, 20 | options => options.UseMsgPackFormatter() 21 | ) 22 | .ConfigureServices(services => 23 | { 24 | services 25 | .AddControllers() 26 | .AddJil() 27 | .AddMsgPack() 28 | .AddProtobuf() 29 | .AddUtf8Json() 30 | .AddZeroFormatter() 31 | .AddXmlSerializerFormatters(); 32 | services.AddSwaggerDocument(); 33 | }) 34 | .Configure(app => 35 | { 36 | app.UseHttpsRedirection() 37 | .UseOpenApi() 38 | .UseSwaggerUi() 39 | .UseAoxe() 40 | .UseRouting() 41 | .UseAuthorization() 42 | .UseEndpoints(endpoints => 43 | { 44 | endpoints.MapControllers(); 45 | }); 46 | }) 47 | .UseUrls("http://localhost:5002") 48 | .Run(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Zeroformatter/Aoxe.Client.Http.Zeroformatter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | The zeroformatter formatter for Aoxe http client. 8 | Aoxe;MicroService;Http Client;Formatter;Zeroformatter 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.OpenApi.Models; 7 | 8 | namespace BackendForBrowser 9 | { 10 | public class Startup 11 | { 12 | public Startup(IConfiguration configuration) 13 | { 14 | Configuration = configuration; 15 | } 16 | 17 | public IConfiguration Configuration { get; } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | services.AddControllers(); 23 | services.AddSwaggerGen(c => 24 | { 25 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "BackendForBrowser", Version = "v1" }); 26 | }); 27 | 28 | services.AddQueryService() //批量注册QueryService 29 | .AddDbConnection(Configuration); //Q端连接从库 30 | } 31 | 32 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 33 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 34 | { 35 | if (env.IsDevelopment()) 36 | { 37 | app.UseDeveloperExceptionPage(); 38 | app.UseSwagger(); 39 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "BackendForBrowser v1")); 40 | } 41 | 42 | app.UseHttpsRedirection(); 43 | 44 | app.UseRouting(); 45 | 46 | app.UseAuthorization(); 47 | 48 | app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters/Aoxe.AspNetCore.Formatters.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0; 5 | 2024.1.1 6 | 2024.1.1 7 | Library 8 | true 9 | The formatters for asp.net core base by Aoxe.Serializers 10 | Aoxe;asp.net core formatters 11 | https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters 12 | https://github.com/AoxeTech/Aoxe 13 | MIT 14 | latest 15 | enable 16 | enable 17 | git 18 | true 19 | snupkg 20 | true 21 | true 22 | true 23 | true 24 | Mutuduxf 25 | 26 | 27 | 28 | 29 | 30 | all 31 | runtime; build; native; contentfiles; analyzers; buildtransitive 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/Client/Http/Formatters/Aoxe.Client.Http.Formatter/AoxeHttpClientTextFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Formatter; 2 | 3 | public class AoxeHttpClientTextFormatter : AoxeHttpClientFormatter 4 | { 5 | private readonly ITextSerializer _serializer; 6 | public string MediaType { get; } 7 | 8 | public AoxeHttpClientTextFormatter(AoxeClientFormatterOptions options) 9 | { 10 | if (options.Serializer is not ITextSerializer textSerializer) 11 | throw new ArgumentException("The Serializer is not ITextSerializer."); 12 | _serializer = textSerializer; 13 | MediaType = options.MediaType; 14 | } 15 | 16 | public override HttpRequestMessage CreateHttpRequestMessage(string requestUri, object? message) 17 | { 18 | var httpContent = new StringContent(_serializer.ToText(message), Encoding.UTF8, MediaType); 19 | return CreateHttpRequestMessage(httpContent, MediaType, requestUri); 20 | } 21 | 22 | public override async Task GetResultAsync( 23 | Type returnType, 24 | HttpResponseMessage httpResponseMessage 25 | ) 26 | { 27 | var result = await httpResponseMessage.Content.ReadAsStringAsync(); 28 | var type = 29 | returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>) 30 | ? returnType.GenericTypeArguments[0] 31 | : returnType; 32 | if (httpResponseMessage.IsSuccessStatusCode) 33 | return string.IsNullOrWhiteSpace(result) ? null : _serializer.FromText(type, result); 34 | 35 | var aoxeError = _serializer.FromText(result)!; 36 | throw new AoxeException(aoxeError.Message) 37 | { 38 | Id = aoxeError.Id, 39 | Code = aoxeError.Code, 40 | ThrowTime = aoxeError.ThrowTime, 41 | Source = aoxeError.Source 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Client/Http/Aoxe.Client.Http/Aoxe.Client.Http.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | Client for Aoxe framework base by http. 8 | Aoxe;MicroService;Http Client 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Jil/Aoxe.AspNetCore.Formatters.Jil.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | Library 8 | true 9 | Jil formatters for asp.net core 10 | Aoxe;asp.net core formatters;jil 11 | https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters 12 | https://github.com/AoxeTech/Aoxe 13 | MIT 14 | latest 15 | enable 16 | enable 17 | git 18 | true 19 | snupkg 20 | true 21 | true 22 | true 23 | true 24 | Mutuduxf 25 | 26 | 27 | 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.MsgPack/Aoxe.AspNetCore.Formatters.MsgPack.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | Library 8 | true 9 | MsgPack formatters for asp.net core 10 | Aoxe;asp.net core formatters;MsgPack 11 | https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters 12 | https://github.com/AoxeTech/Aoxe 13 | MIT 14 | latest 15 | enable 16 | enable 17 | git 18 | true 19 | snupkg 20 | true 21 | true 22 | true 23 | true 24 | Mutuduxf 25 | 26 | 27 | 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.Utf8Json/Aoxe.AspNetCore.Formatters.Utf8Json.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | Library 8 | true 9 | Utf8Json formatters for asp.net core 10 | Aoxe;asp.net core formatters;Utf8Json 11 | https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters 12 | https://github.com/AoxeTech/Aoxe 13 | MIT 14 | latest 15 | enable 16 | enable 17 | git 18 | true 19 | snupkg 20 | true 21 | true 22 | true 23 | true 24 | Mutuduxf 25 | 26 | 27 | 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi/ErrorHandlingMiddleware.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.WebApi; 2 | 3 | internal class ErrorHandlingMiddleware(RequestDelegate next) 4 | { 5 | public async Task Invoke(HttpContext context) 6 | { 7 | try 8 | { 9 | await next(context); 10 | } 11 | catch (AoxeException ex) 12 | { 13 | await HandleExceptionAsync(context, ex, 600); 14 | } 15 | catch (ArgumentNullException ex) 16 | { 17 | await HandleExceptionAsync(context, ex, 600); 18 | } 19 | catch (ArgumentOutOfRangeException ex) 20 | { 21 | await HandleExceptionAsync(context, ex, 600); 22 | } 23 | catch (ArgumentException ex) 24 | { 25 | await HandleExceptionAsync(context, ex, 600); 26 | } 27 | catch (ApplicationException ex) 28 | { 29 | await HandleExceptionAsync(context, ex, 600); 30 | } 31 | catch (Exception ex) 32 | { 33 | await HandleExceptionAsync(context, ex, 600); 34 | } 35 | } 36 | 37 | private static Task HandleExceptionAsync(HttpContext context, Exception ex, int httpStatusCode) 38 | { 39 | var inmostEx = ex; 40 | while (inmostEx.InnerException is not null) 41 | inmostEx = inmostEx.InnerException; 42 | context.Response.StatusCode = httpStatusCode; 43 | var aoxeError = new AoxeError 44 | { 45 | Id = inmostEx is AoxeException aoxeException ? aoxeException.Id : Guid.NewGuid(), 46 | Message = inmostEx.Message, 47 | Source = inmostEx.Source ?? string.Empty, 48 | StackTrace = inmostEx.StackTrace ?? string.Empty, 49 | ThrowTime = DateTime.Now 50 | }; 51 | if (aoxeError == null) 52 | throw new ArgumentNullException(nameof(aoxeError)); 53 | return context.Response.WriteAsync(JsonSerializer.Serialize(aoxeError)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Server/WebApi/Aoxe.WebApi.Formatters/Aoxe.AspNetCore.Formatters.ZeroFormatter/Aoxe.AspNetCore.Formatters.ZeroFormatter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 2024.1.0 6 | 2024.1.0 7 | Library 8 | true 9 | Formatters for asp.net core base by ZeroFormatter. 10 | Aoxe;asp.net core formatters;ZeroFormatter 11 | https://github.com/AoxeTech/Aoxe/tree/master/src/Server/Formatters/Aoxe.AspNetCore.Formatters 12 | https://github.com/AoxeTech/Aoxe 13 | MIT 14 | latest 15 | enable 16 | enable 17 | git 18 | true 19 | snupkg 20 | true 21 | true 22 | true 23 | true 24 | Mutuduxf 25 | 26 | 27 | 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD/Aoxe.DDD.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 2024.3.2 6 | 2024.3.2 7 | DDD framework for Aoxe. 8 | Aoxe;DDD;Framework 9 | https://github.com/AoxeTech/Aoxe 10 | https://github.com/AoxeTech/Aoxe 11 | MIT 12 | latest 13 | enable 14 | enable 15 | git 16 | true 17 | snupkg 18 | true 19 | true 20 | true 21 | true 22 | Mutuduxf 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | all 36 | runtime; build; native; contentfiles; analyzers; buildtransitive 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /demo/Aoxe.DDD/BackendForFrontend/BackendForBrowser/README.md: -------------------------------------------------------------------------------- 1 | # Backend for frontend 2 | 3 | --- 4 | 5 | ## 缘起 6 | 7 | 客户端(Public API/Browser/Mobile)通过多次向微服务发起请求完成业务流程的缺点是众所周知的,因此在微服务的体系中我们需要在内网与外网间添加一层用于后台微服务的API聚合,此实现即为API网关。 8 | 9 | ![img](images/Richardson_tAGP_01.png) 10 | 11 | API网关负责请求路由、API组合以及协议转换。 来自外部客户端的所有API请求首先都会到达API网关。 API网关将一些请求路由到适当的服务。 网关使用API组合模式并调用多个服务并汇总结果来处理其他请求。 它还可能会在服务友好的客户端友好协议(如restful)与客户端不友好协议(WebService、grpc?、thrift)之间进行转换。 12 | 13 | * 请求路由:API网关的关键功能之一是请求路由。 API网关通过将请求路由到相应的服务来实现一些API操作。 当API网关接收到请求时,它会查询路由映射,该路由映射指定将请求路由到的服务。 路由映射例如可以将HTTP方法和路径映射到服务的HTTP URL。 此功能与Web服务器(如NGINX)提供的反向代理功能相同。 14 | 15 | * API组合:API网关通常做的不只是反向代理, 它还可能会使用API组合来实现一些API操作。 我们想象下直接将微服务暴露在外网的情形:客户端需要完成单个业务流程,此业务流程需要横跨多个不同的微服务(包括界面刷新)从而导致数十次请求,在这个过程中我们后台服务器浪费了几倍的吞吐资源,因此我们需要在客户端和后台微服务之间添加一个API组合的层,降低请求频次。 16 | 17 | * 协议转换:客户端通常要求使用restful风格设计的接口,但后台微服务或出于团队协作的考虑,或处于性能原因,可能会采用其它的RPC设计(如RPC风格的http、grpc以及thrift等) 18 | 19 | * 实现边缘功能:如上图所示,微服务是依据业务的纵向分离,API网关将微服务隔离在内网并作为整个系统的功能设计暴露在公网,因此我们可以将一些边缘功能实现在API网关上,如: 20 | 21 | 1. 身份验证——验证发出请求的客户端的身份 22 | 2. 授权——验证客户端是否有权执行特定操作 23 | 3. 速率限制——限制特定客户端和/或所有客户端每秒允许多少个请求 24 | 4. 缓存——缓存响应以减少对服务的请求数量 25 | 5. 指标收集——收集有关API使用情况的指标以用于热点或者计费分析 26 | 6. 请求记录——记录请求日志 27 | 28 | ## BFF 29 | 30 | Backendf For Frontend属于特化的API网关,其相对于API网关多了一个职责——针对客户端(前端)进行API组合设计。虽然API网关有API组合的职责,但其一般是比较范用的设计。如不同的客户端(如Browser和IOS)请求时,移动端由于界面的考虑其实并不需要Browser那么多的数据,甚至移动端的业务操作会相对弱化避免太过复杂,在此情况下移动端使用和Browser一样的API设计就不太合适了。 31 | 32 | 如果所有客户端的API都实现在一个API网关上往往会导致API网关过重,甚至团队协作间产生冲突(如移动端需要添加新的API,却连同Browser端的代码都需要一起发布),甚至会导致管理上的困难(依据**谁开发谁拥有**的原则,假如所有客户端团队都在同一个API网关上进行开发,则所有客户端团队都需要对同一个代码仓库负责;如果由单一的团队(如API网关团队甚至是后台团队)负责API网关的开发,则其必须了解所有客户端的API需求,并且要和所有客户端团队联动响应,这是不太现实的,也是比较低效的),因此对应不同的客户端,会有不同的BFF。 33 | 34 | 因应客户端的需求而设计的BFF,其最佳实践是由对应的客户端团队负责,而且考虑到边缘功能的通用,理想情况下最好使用相同的技术栈(如nodejs)。除了明确定义职责外,BFF模式还具有其他好处:API模块彼此隔离,从而提高了可靠性。 一个行为不当的API不会轻易影响其他API。 BFF模式还提高了可观察性,因为不同的API模块是不同的过程。 BFF模式的另一个好处是每个API都是独立可伸缩的。 BFF模式减少了启动时间,因为每个API网关都是一个更小、更简单的应用程序。以下两张图展示了BFF在团队中的演化: 35 | 36 | ![img](images/Richardson_tAGP_04.png) 37 | 38 | ![img](images/Richardson_tAGP_05.png) -------------------------------------------------------------------------------- /src/Client/Http/Aoxe.Client.Http/Internal/ZaabyClientProxy.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.Client.Http.Internal; 2 | 3 | internal class AoxeClientProxy : DispatchProxy 4 | { 5 | internal Type InterfaceType { get; set; } = null!; 6 | internal HttpClient Client { get; set; } = null!; 7 | internal AoxeHttpClientFormatter HttpClientFormatter { get; set; } = null!; 8 | 9 | internal static object Create(Type interfaceType) => 10 | typeof(DispatchProxy) 11 | .GetMethod(nameof(DispatchProxy.Create))! 12 | .MakeGenericMethod(interfaceType, typeof(AoxeClientProxy)) 13 | .Invoke(null, null)!; 14 | 15 | protected override object? Invoke(MethodInfo? targetMethod, object?[]? args) 16 | { 17 | if (targetMethod is null) 18 | return null; 19 | var result = HttpSendAsync(targetMethod, args?.FirstOrDefault()); 20 | if ( 21 | targetMethod.ReturnType.IsGenericType 22 | && targetMethod.ReturnType.GetGenericTypeDefinition() == typeof(Task<>) 23 | ) 24 | return result.CastResult(targetMethod.ReturnType.GetGenericArguments()[0]); 25 | return result.RunSync(); 26 | } 27 | 28 | private async Task HttpSendAsync(MethodInfo methodInfo, object? message) 29 | { 30 | if (string.IsNullOrEmpty(InterfaceType.FullName)) 31 | throw new AoxeException($"{InterfaceType}'s full name is null or empty."); 32 | var url = $"/{InterfaceType.FullName.Replace('.', '/')}/{methodInfo.Name.TrimEnd("Async")}"; 33 | 34 | var httpRequestMessage = HttpClientFormatter.CreateHttpRequestMessage(url, message); 35 | 36 | var httpResponseMessage = await Client.SendAsync(httpRequestMessage); 37 | 38 | if ( 39 | httpResponseMessage is 40 | { IsSuccessStatusCode: false, StatusCode: not (HttpStatusCode)600 } 41 | ) 42 | throw new AoxeException($"{url}:{httpResponseMessage}"); 43 | 44 | return await HttpClientFormatter.GetResultAsync(methodInfo.ReturnType, httpResponseMessage); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Architecture/DDD/Aoxe.DDD/DomainEventPublisher.cs: -------------------------------------------------------------------------------- 1 | namespace Aoxe.DDD; 2 | 3 | public class DomainEventPublisher(IMessageBus messageBus, IServiceProvider serviceProvider) 4 | : BackgroundService 5 | { 6 | private readonly IServiceProvider _serviceProvider = serviceProvider 7 | .CreateScope() 8 | .ServiceProvider; 9 | 10 | protected override async Task ExecuteAsync(CancellationToken cancellationToken) 11 | { 12 | await using var aoxeDddContext = _serviceProvider.GetService(); 13 | if (aoxeDddContext is null) 14 | throw new NullReferenceException(nameof(aoxeDddContext)); 15 | var lastPublishTime = DateTime.UtcNow; 16 | while (!cancellationToken.IsCancellationRequested) 17 | { 18 | var ms = DateTime.UtcNow - lastPublishTime; 19 | if (ms.Milliseconds < 100) 20 | await Task.Delay(100 - ms.Milliseconds, cancellationToken); 21 | 22 | var unpublishedMessages = aoxeDddContext 23 | .UnpublishedMessages.OrderBy(p => p.PersistenceUtcTime) 24 | .Take(100) 25 | .ToList(); 26 | 27 | foreach (var unpublishedMessage in unpublishedMessages) 28 | await messageBus.PublishAsync(unpublishedMessage.MessageType, unpublishedMessage); 29 | 30 | aoxeDddContext.UnpublishedMessages.RemoveRange(unpublishedMessages); 31 | await aoxeDddContext.PublishedMessages.AddRangeAsync( 32 | unpublishedMessages.Select(p => new AoxePublishedMessage(p)), 33 | cancellationToken 34 | ); 35 | 36 | await aoxeDddContext.SaveChangesAsync(cancellationToken); 37 | lastPublishTime = DateTime.UtcNow; 38 | } 39 | } 40 | 41 | public override async Task StopAsync(CancellationToken cancellationToken) 42 | { 43 | if (messageBus is IDisposable disposable) 44 | disposable.Dispose(); 45 | if (messageBus is IAsyncDisposable asyncDisposable) 46 | await asyncDisposable.DisposeAsync(); 47 | await base.StopAsync(cancellationToken); 48 | } 49 | } 50 | --------------------------------------------------------------------------------