{"id":22191583,"url":"https://github.com/vip32/naos","last_synced_at":"2025-07-26T22:31:28.155Z","repository":{"id":35819410,"uuid":"152266928","full_name":"vip32/Naos","owner":"vip32","description":"A mildly opiniated modern cloud service architecture blueprint + reference implementation","archived":false,"fork":false,"pushed_at":"2022-12-08T09:09:21.000Z","size":6662,"stargazers_count":20,"open_issues_count":14,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-08T16:30:15.411Z","etag":null,"topics":["azure","c-charp","commands-queries","correlation","ddd-architecture","distributed-systems","domain-driven-design","logging","messaging","microservices","operations","queueing","repositories","scheduled-jobs","servicediscovery","specifications","storage"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vip32.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-09T14:34:53.000Z","updated_at":"2023-10-06T05:13:30.000Z","dependencies_parsed_at":"2022-08-29T04:20:26.119Z","dependency_job_id":null,"html_url":"https://github.com/vip32/Naos","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vip32%2FNaos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vip32%2FNaos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vip32%2FNaos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vip32%2FNaos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vip32","download_url":"https://codeload.github.com/vip32/Naos/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227724162,"owners_count":17810036,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure","c-charp","commands-queries","correlation","ddd-architecture","distributed-systems","domain-driven-design","logging","messaging","microservices","operations","queueing","repositories","scheduled-jobs","servicediscovery","specifications","storage"],"created_at":"2024-12-02T12:16:44.294Z","updated_at":"2024-12-02T12:16:45.179Z","avatar_url":"https://github.com/vip32.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://vip32.visualstudio.com/Naos/_apis/build/status/vip32.Naos?branchName=master)](https://vip32.visualstudio.com/Naos/_build/latest?definitionId=4\u0026branchName=master)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vip32_Naos\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=vip32_Naos)\n[![CodeFactor](https://www.codefactor.io/repository/github/vip32/Naos/badge)](https://www.codefactor.io/repository/github/vip32/Naos)\n[![codecov](https://codecov.io/gh/vip32/Naos/branch/master/graph/badge.svg)](https://codecov.io/gh/vip32/Naos)\n[![GitHub issues](https://img.shields.io/github/issues/vip32/Naos.svg)](https://github.com/vip32/Naos/issues)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/vip32/Naos/master/LICENSE)\n\n![logo](docs/logo5.png)\n\n\u003cp align=\"center\"\u003e\u003ch1\u003eA mildly opiniated modern cloud service architecture blueprint \u0026 reference implementation\u003c/h1\u003e\u003c/p\u003e\n\n# Dev stack\n\nC#, .Net Core 2.x, EnsureThat, Serilog, Mediator, FluentValidation, AutoMapper, xUnit, Shouldly, NSubstitute\n\n## Architectural concepts\n\n- architectural style: hexagonal/onion\n- [Domain Drive Design](https://martinfowler.com/tags/domain%20driven%20design.html)\n- pattern: Domain Events\n- pattern: Domain Entity\n- pattern: Domain Value Object\n- pattern: Repository (inmemory, cosmosdb, entityframework)\n- pattern: Decorators\n- pattern: Specifications\n- pattern: Query Object (Criteria)\n- pattern: Layer Supertype (Entity)\n- pattern: Commands\n- pattern: Queries\n- pattern: Messaging (servicebus, signalr, filesystem, rabbitmq)\n\n#### Service API\n\n- Style:\n  - Pragmatic REST: resources, http verbs, crud + actions/commands\n  - RPC: commands?\n- Patterns:\n\n  - Stateless: clients maintain state [Controller]\n  - Facade: the API acts as a facade, domain logic exists beyond it [Controller]\n  - Proxy: wrapper for external entities (+validation, +transformation) [ServiceDiscoveryClient]\n\n#### Messaging\n\n- Patterns:\n  - Competing workers,\n  - Fanout\n\n# Getting started\n\n## Sample\n\n[^](SAMPLE.md)\n\n## Secrets Setup\n\nSetup key vault:\n- Create a key vault [^](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-get-started) [=RESOURCE_NAME]\n- Register an application with Azure Active Directory [^](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-get-started) [=AAD_APPLICATION_ID]\n- Create a client secret for the application (Certificates \u0026 secrets) [=AAD_APPLICATION_KEY]\n\nAdd key vault access policy:\n- `Set-AzureRmKeyVaultAccessPolicy -VaultName '[RESOURCE_NAME]' -ServicePrincipalName [AAD_APPLICATION_ID] -PermissionsToKeys decrypt,sign,get,unwrapKey,list -PermissionsToSecrets get,list -PermissionsToCertificates get,list`\n- `az keyvault set-policy --name [RESOURCE_NAME] --certificate-permissions get list --key-permissions get list decrypt unwrapKey verify --object-id [AAD_APPLICATION_ID] --secret-permissions get list`\n\nor\n\n- Open createad Key Vault in Azure Portal\n- Select Access Policies\n- Click the [+ Add new] button at the top of the blade\n- Click Select Principal to select the created Azure Active Directory Application\n- From the Key permissions drop down, select \"Decrypt\", \"Sign\", \"Get\", \"UnwrapKey\" permissions\n- Save changes\n\n##### Local setup\n\n- (1) Either Store application clientId (AAD_APPLICATION_ID) and clientSecret (AAD_APPLICATION_KEY) in environment variables\n  - `naos__secrets__vault__name=[RESOURCE_NAME]`\n  - `naos__secrets__vault__clientId=[AAD_APPLICATION_ID]`\n  - `naos__secrets__vault__clientSecret=[AAD_APPLICATION_KEY]`\n- (2) Or store application clientId (AAD_APPLICATION_ID) and clientSecret (AAD_APPLICATION_KEY) in an user secrets files\n- (3) Or store the application clientId (AAD_APPLICATION_ID) and clientSecret (AAD_APPLICATION_KEY) in the appsettings file\n- Authorize the application to use the key or secret [^](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-get-started)\n\noptionally use a multitude of the usual [netcore configuration providers](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1#providers) to store the settings\n\n(2) When using the [usersecrets](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets) configuration provider use the following json file:\n\n```\n{\n  \"naos:secrets:vault:name\": \"[RESOURCE_NAME]\",\n  \"naos:secrets:vault:clientId\": \"[AAD_APPLICATION_ID]\",\n  \"naos:secrets:vault:clientSecret\": \"[AAD_APPLICATION_KEY]\",\n}\n```\n\nPlace the file in the following location.\n`C:\\Users\\[USERNAME]\\AppData\\Roaming\\Microsoft\\UserSecrets\\[SECRETSID]\\secrets.json`\nThe [SECRETSID] should be a guid and must be configured in the appsettings file.\n\n```\n{\n  \"naos\": {\n    \"secrets\": {\n      \"userSecretsId\": \"[SECRETSID]\"\n    }\n  }\n}\n```\n\n(3) When using the [file](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1#file-configuration-provider) configuration provider:\n\n```\n{\n  \"naos\": {\n    \"secrets\": {\n      \"vault\": {\n        \"name\": \"[RESOURCE_NAME]\",\n        \"clientId\": \"[AAD_APPLICATION_ID]\",\n        \"clientSecret\": \"[AAD_APPLICATION_KEY]\"\n      }\n    }\n  }\n}\n```\n\n##### Azure Setup\n\n- Store application clientId (ApplicationId) and clientSecret (ApplicationKey) in the App Service application settings (Environment):\n  - `naos__secrets__vault__name=[VAULT_NAME]`\n  - `naos__secrets__vault__clientId=[AAD_APPLICATION_ID]`\n  - `naos__secrets__vault__clientSecret=[AAD_APPLICATION_KEY]`\n\n## Service Setup\n\n#### Dependencies\n\n- nugets ?? (meta packages)\n\n#### Configuration\n\n- TODO (appsettings)\n- InProcess [*](https://weblog.west-wind.com/posts/2019/Mar/16/ASPNET-Core-Hosting-on-IIS-with-ASPNET-Core-22#outofprocess-or-inprocess-use-inprocess)\n\n#### Bootstrapping\n\n- Program.cs\n\n```\n        public static void Main(string[] args)\n        {\n            CreateWebHostBuilder(args).Build().Run();\n        }\n\n        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =\u003e\n            WebHost.CreateDefaultBuilder(args)\n                .ConfigureAppConfiguration((context, config) =\u003e\n                {\n                    NaosConfigurationFactory.Extend(config);\n                })\n                .UseStartup\u003cStartup\u003e()\n                .UseSerilog();\n```\n\n- Startup.cs\n\n```\nTODO\n```\n\n# Service Features Overview\n\n![overview0](docs/Naos%20–%20Architecture%20Overview.png)\n\n# Service External View\n\nA service is a standalone, independently deployable software component that implements useful functionality (seperated by [Bounded Context](https://martinfowler.com/bliki/BoundedContext.html)).\nThe external view of the service looks like this:\n![overview](docs/Naos%20–%20Service%20External%20View.png)\nThe service has an API (inbound) thats provides its consumers access to its functionality. This API can consist API of the normal REST interface, or even RPC style if needed.\nBesides the usual API Commands and Queries can be used. A command performs actions and updates data. A query retrieves data.\n\n\u003cp\u003eAlso the service itself has some ways to interact with other services (outbound).\nBy using a ServiceClient (wiht ServiceDiscovery) other services can be consumed (Rest/Rpc)\nA service can subscribe to specific messages, but can also publish messages byitself.\n\u003c/p\u003e\n\nA service's (inboud) API encapsulates its internal implementation. The service cannot be used in any other way, this API enforces the\napplication's modularity.\n\n# Microservice\n\n### Benefits\n\n- Independent development: Developers can work on different microservices at the same time and choose the best technologies for the problem they are solving.\n- Independent release and deployment: Services can be updated individually on their own schedule.\n- Granular scaling: Individual services can scale independently, reducing the overall cost and increasing reliability.\n- Simplicity: Smaller services are easier to understand which expedites development, testing, debugging, and launching a product.\n- Fault isolation: Failure of a service does not have to translate into failure of other services.\n\n### Design Principles\n\n- High cohesion\n- Low coupling\n- Autonomous\n- Domain centric\n- Resiliency\n- Observable\n- Automation\n\n# Layers \u0026 Dependencies\n\n```\n  .-------------.\n  | Application | - Commands + Handlers \n  | Layer       | - Messages + Handlers\n  |             | - REST Controllers\n  \"-------------\" \n     |     | \n     |     |      .-------------.\n     |     \"--- \u003e |    Domain   | - Models\n     |            |    Layer    | - Repositories\n     |            |             |\n     |            \"-------------\"\n     |                 ^\n     V                 |\n   .----------------.  |\n   | Infrastructure |--\"\n   | Layer          |\n   |                | - Repository Implementierungen\n   \"----------------\" \n```\n\n# Service Integration\n```\n    Service A                                  Service B\n  .-----------------.                             .------------------.\n  | .-------------. |                             | .-------------.  |\n  | | Application |-|----------------------------\u003e|\u003e| Application |  |\n  | \"-------------\" |         - HTTP              | \"-------------\"  |\n  |                 |         - Messaging         |                  |\n  |  /\"\"\"\"\"\"\"\"\\     |         - Queueing          |  /\"\"\"\"\"\"\"\"\\      |\n  | / Domain   \\    |                             | / Domain   \\     |\n  | \\  Model   /    |                             | \\  Model   /     | \n  |  \\--------/     |                             |  \\--------/      |\n  |                 |                             |                  |\n  | .-----------.   |                             | .-----------.    |\n  | | Storage   |   |                             | | Storage   |    |\n  | \"-----------\"   |                             | \"-----------\"    |\n  \"-----------------\"                             \"------------------\"\n        TEAM                                              TEAM\n\n```\n\n# Configuration\n\n- Startup\n- KeyVault (+local cache)\n\n# Commands\n\n- Command/Query\n- Command handlers\n- Behaviours\n  - (Decorators)\n\nStartup.cs configuration:\n- ConfigureServices(...)\n```\n.AddNaos(this.Configuration, \"Product\", \"Capability\", new[] { \"All\" }, n =\u003e n\n    ...\n    .AddCommands(o =\u003e o\n        .AddBehavior\u003cValidateCommandBehavior\u003e()\n        .AddBehavior\u003cJournalCommandBehavior\u003e()\n        .AddBehavior(new FileStoragePersistCommandBehavior(\n            new FolderFileStorage(o =\u003e o.Folder(...)))))\n```\n- Configure(...)\n```\n.UseNaos(s =\u003e s\n    ...\n    .UseRequestCommands()\n    ...);\n```\n#### Sending Commands\nUse the mediator (IMediator) and send the command. A registered handler will handle the command and provide a response.\n```\nvar response = await mediator.Send(command).AnyContext();\n```\n\n#### Request Command Dispatcher\nProvides HTTP access to specific commands, these commands need to be registered. The commands are available on endpoints as specified in the AddCommands configuration.\nAll configured commands are also available in the swagger documentation (with request/response schemas).\n```\n\n                   CommandRequest\n H               .----------------.                                                           CommandHandler\n T               | -Id            |     RequestCommandDispatcher                             .--------------.\n T-------------\u003e .----------------.     Middleware                 Mediator             .--\u003e | Handle()     |\n P   (request)   | -CorrelationId |---\u003e.------------.              .------------.      /     `--------------`\n                 `----------------`    | Invoke()   |-------------\u003e| Send()     |-----`             |\n  \u003c------------------------------------|            |\u003c-------------|            |\u003c----.             V\n     (response)                        `------------`              `------------`      \\      CommandResponse\n                                     (match method/route)                               \\    .--------------.\n                                                                                         `---| -Result      |\n                                                                                             | -Cancelled   |\n                                                                                             `--------------`\n                                                                                                 (result)\n```\n\nStartup.cs configuration:\n- ConfigureServices(...)\n```\n.AddNaos(this.Configuration, \"Product\", \"Capability\", new[] { \"All\" }, n =\u003e n\n    ...\n    .AddCommands(o =\u003e o\n        ...\n        .AddRequestDispatcher(o =\u003e o\n            .Post\u003cCreateCustomerCommand\u003e(\"/api/commands/customers/create\", 201)\n            .Get\u003cGetActiveCustomersQuery, IEnumerable\u003cCustomer\u003e\u003e(\"/naos/commands/customers/active\", 200)))\n```\n\n\n# Messaging\n\n- Message\n- Message handlers\n- Message brokers\n  - Azure Eventbus\n  - SignalR\n  - Local filesystem\n- Publish DomainEvent as Message (DomainEventMessagingPublisher)\n```\n .----.                                                                       .\n | a  |    .----.                              .----------.                  /\n \"----\"    | c  |             .--------.       | Domain   |                 /\n      |    \"----\"   x-------\u003e | Domain |       | Event    |                /\n    .----.   /                | Event  x-----\u003e | Message  |            .----------.\n    | b  |--\"                 \"--------\"       | PUBLISHER|            | Message  |\n    \"----\"                                     |----------|            | Broker   |\n  Domain Model                                 |-Map()    |            |----------|                  External Service\n                                               |-Handle() x----------\u003e |-Publish()|             .-----------------------.\n                                               |          |            \"----x-----\"            / .----. Domain Model   /\n                                               \"----------\"          /      |  +message       /  | x  |    .----.     /\n                                                                    /       \"--------------\u003e /   \"----\"    | y  |    /\n    Internal Service (origin                                       /              subscribed/         |    \"----\"   /\n -----------------------------------------------------------------\"                        /        .----.   /     /\n                                                                                          /         |  z |--\"     /\n                                                                                         /          \"----\"       /\n                                                                                        \"-----------------------\"\n```\n\n### Messaging setup (Azure ServiceBus)\n\n**NOTE**:\nNaos:Messaging needs to administer the Azure ServiceBus by using the [Azure Resource Manager](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview) API. To use this API some [extra setup](https://tomasherceg.com/blog/post/azure-servicebus-in-net-core-managing-topics-queues-and-subscriptions-from-the-code) has to be\ndone.\n\n- create a servicebus (standard pricing)\n- `Get-AzureRmSubscription` (=subscriptionId/tenantId)\n- `$sp = New-AzureRmADServicePrincipal -DisplayName \"PRINCIPAL_NAME\"`\n- `Write-Host \"clientid:\" $sp.ApplicationId` (=clientId)\n- `$pBSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sp.Secret)`\n- `$p = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($pBSTR)`\n- `Write-Host \"clientsecret:\" $p` (=clientSecret)\n- `New-AzureRmRoleAssignment -ServicePrincipalName $sp.ApplicationId -ResourceGroupName RESOURCE_GROUP -ResourceName RESOURCE_NAME -RoleDefinitionName Contributor -ResourceType Microsoft.ServiceBus/namespaces`\n\nkey vault keys: (or use the json configuration above)\n\n- `development-naos--messaging--serviceBus--subscriptionId`\n- `development-naos--messaging--serviceBus--tenantId`\n- `development-naos--messaging--serviceBus--connectionString`\n- `development-naos--messaging--serviceBus--resourceGroup` (=RESOURCE_GROUP)\n- `development-naos--messaging--serviceBus--namespaceName` (=RESOURCE_NAME)\n- `development-naos--messaging--serviceBus--clientId`\n- `development-naos--messaging--serviceBus--clientSecret`\n\n# Queueing\n\n- Enqueue\n  ```\n  await this.queue.EnqueueAsync(\n      new EchoQueueEventData { Message = \"+++ hello from queue item +++\" }).AnyContext();\n  ```\n- Dequeue\n  ```\n  var item = await queue.DequeueAsync();\n  Console.WriteLine(item.Data.Message);\n  ```\n- Queue Processing (ProcessItemsAsync)\n  - Handle by using a function\n    ```\n\tawait queue.ProcessItemsAsync(async i =\u003e\n        {\n            Console.WriteLine(i.Data.Message);\n            await i.CompleteAsync();\n        });\t\n    ```\n  - Handle by sending events which a single handler can pick up (Mediatr)\n    ```\n\tawait this.queue.ProcessItemsAsync(true).AnyContext();\n\t```\n\twill be handled by 'EchoQueueEventHandler' (EchoQueueEventData)\n\n- Implementations\n  - InMemory Queue\n  - Azure Queue Storage\n  - Azure ServiceBus\n- Decorators\n\nCross Service usage:\n![inter service](docs/Naos%20–%20Queueing.png)\n```\n        .-------.                       .-------.\n       /         \\                     /         \\\n      /  Service  \\                   /  Service  \\\n      \\  A    .-----------.      .-----------. B  / \n       \\......| Queueing  |      | Queueing  |.../ \n              | \u003cenqueue\u003e |      | \u003cdequeue\u003e | - EchoQueueEventHandler\n              \"---x-------\"      \"-------x---\"\n + EchoQueueEventData                    |\n                  |      .-----------.   |\n                  `-----\u003e| Queueing  |\u003c--`\n                         | \u003cenqueue\u003e |\n                         \"-----------\"\n```\n\nInside Service usage:\n![inside service](docs/Naos%20–%20Queueing2.png)\n```\n         + EchoQueueEventData\n              .-----------.\n              | Queueing  x-----------------.\n        .-----| \u003cenqueue\u003e |                 |\n       /      \"-----------\"            .----V--------.\n      /           \\                    | Queue       |\n      |  Service  |                    | (storage/   |\n      |  A        |                    |  servicebus)|\n      \\       .-----------.            \"----^--------\"\n       \\......| Queueing  |                 |\n              | \u003cdequeue\u003e |-----------------\"      \n              \"-----------\"      \n         - EchoQueueEventHandler\n```\n\n# Domain\n\n- Model\n\n  - [Aggregate](https://martinfowler.com/bliki/DDD_Aggregate.html) [^](https://deviq.com/aggregate-pattern/)\n    \u003cp\u003eAn aggregate is a collection of one or more related entities (and possibly value objects). Each aggregate has a single root entity, referred to as the aggregate root. The aggregate root is responsible for controlling access to all of the members of its aggregate. It’s perfectly acceptable to single-entity aggregates, in which case that entity is itself the root of its aggregate. In addition to controlling access, the aggregate root is also responsible for ensuring the consistency of the aggregate.\u003c/p\u003e\n    \u003cp\u003eWhen applying the aggregate pattern, it is also important that persistence operations apply only at the aggregate root level. Thus, the aggregate root must be an entity, not a value object, so that it can be persisted to and from a data store using its ID\u003c/P\n  - [Domain Entity](https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/microservice-domain-model) [^](https://deviq.com/entity/)\n\n    \u003cp\u003eAn Entity is an object that has some intrinsic identity, apart from the rest of its state. Even if its properties are the same as another instance of the same type, it remains distinct because of its unique identity.\u003c/p\u003e\n\n    ![domainentity](https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/media/image9.png)\n\n  - [ValueObject](https://martinfowler.com/bliki/ValueObject.html) [^](https://deviq.com/value-object/)\n    \u003cp\u003eA Value Object is an immutable type that is distinguishable only by the state of its properties. That is, unlike an Entity, which has a unique identifier and remains distinct even if its properties are otherwise identical, two Value Objects with the exact same properties can be considered equal.\u003c/p\u003e\n  - Specification [^](https://deviq.com/specification-pattern/)\n    \u003cp\u003eA solution to the problem of where to place querying logic is to use a Specification. The Specification design pattern describes a query in an object.\n    Repository methods (FindAll) will accept an ISpecification and will be able to produce the expected result given the specification.\n    \u003c/p\u003e\n\n- Domain Events\n  - Handlers\n\n# Domain Repository\n\n\u003cp\u003e\nProvides an abstraction of data, so that your service can work with a simple abstraction that has an interface approximating that of a collection.\nAdding, removing, updating, and selecting items from this collection is done through a series of straightforward methods (FindAll, FindOne, Upsert),\nwithout the need to deal with database/storage concerns like connections, commands, cursors, or readers. Using this pattern can help achieve loose coupling and\ncan keep domain objects persistence ignorant.\n\u003c/p\u003e\n\n[^](https://deviq.com/repository-pattern/)\n\n- IReadonlyRepository\u003cT\u003e\n- IRepository\u003cT\u003e\n- Specification\u003cT\u003e\n- IFindOptions\u003cT\u003e\n- Repositories\n  - InMemory\n  - CosmosDb (Document)\n  - EntityFramework (Sql, Sqlite)\n- Decorators\n```\n .-----------.\n | Decorator |\n `-----------`        .------------.\n       `------------\u003e | decoratee  |\n         (forward)    `------------`\n``` \n\n# Domain Specifications\n\n- Specification\u003cT\u003e\n- (CriteriaSpecification(IENumerable\u003cCrits\u003e))\n\n# ServiceContext\n\n- Product\n- Capability\n\n# RequestCorrelation\n\n# RequestFiltering\n\n- Criteria\n- Order\n- Skip/Take\n\nsome example filtered requests:\n\n```\nGET https://localhost:5001/api/countries?q=name=Belgium\u0026order=name\u0026take=1\n```\n\n# ServiceExceptions\n\n- Middleware: ConfigureServices() + service.AddNaos().AddServiceExceptions()\n- ProblemDetails [^](https://tools.ietf.org/html/rfc7807)\n- ResponseHandlers (based on specific exceptions)\n  - BadRequestException handled by BadRequestExceptionResponseHandler with ValidationProblemDetails response\n  - ValidationException handled by ClientFormatExceptionResponseHandler with ValidationProblemDetails response\n  - ClientFormatException handled by FluentValidationExceptionResponseHandler with ValidationProblemDetails response\n  - BadHttpRequestException handled by KestrelExceptionResponseHandler with ProblemDetails response\n- Create your own handler by implementing Naos.Commands.Exceptions.Web.IExceptionResponseHandler\n\nexample http response when requesting an invalid criteria (or ordering) name:\n\n```\nGET https://localhost:5001/api/countries?q=nameX=Belgium\u0026order=name\u0026take=1\n{\n\"errors\": {},\n\"type\": \"Naos.Common.NaosClientFormatException\",\n\"title\": \"A request content client format error has occurred while executing the request\",\n\"status\": 400,\n\"detail\": \"'nameX' is not a member of type 'Naos.Sample.Countries.Domain.Country'\",\n\"instance\": \"7SOLEZT597QXB (SSFSG)\"\n}\n```\n\n# ServiceDiscovery\n\nService discovery is conceptually quite simple: its key component is the registry, which is a database of the network locations of the available service instances.\n\nThe service discovery mechanism updates the service registry when service instances start and stop. When a ServiceClient invokes a service, the service discovery mechanism queries the registry to obtain a list of available service instances and routes the request to one of them.\n\n## Client-side (Client)\n\nA service client retrieves the list of available service instances from the service registry and load balances across them.\n\n- Registration: Services register themselves in the registry on startup and deregister on shutdown.\n- ServiceClient: Uses registry to find correct and healthy endpoints for services. Selection is done based on name or tags\n- Health: periodically check registered services\n\n### Registries\n\n- FileSystem\n- Consul\n- (TODO: CosmosDb/Azure Storage Table)\n\n![overview1](docs/Naos%20–%20Service%20Discovery%20Overview%20-%20Client-side.png)\n\n## Server-side (Router)\n\nA service client makes a request to a router, which is responsible for service discovery and forwarding (reverse-proxy).\n\n- Router\n- Registration\n- ServiceClient\n- Health\n\n### Registries\n\nThe same registries as Client-side can be used in the router.\n\n- FileSystem\n- Consul\n- (TODO: CosmosDb/Azure Storage Table)\n\n![overview2](docs/Naos%20–%20Service%20Discovery%20Overview%20-%20Server-side.png)\n\n# Operations\n\n### Logging\n  - Debug\n  - Console\n  - File\n    - Rolling\n  - Azure DiagnosticsLogStream\n  - Azure ApplicationInsights\n  - Azure LogAnalytics\n   \n```\ndevelopment-naos--operations--logging--azureLogAnalytics--apiAuthentication--clientId\ndevelopment-naos--operations--logging--azureLogAnalytics--apiAuthentication--clientSecret\ndevelopment-naos--operations--logging--azureLogAnalytics--apiAuthentication--tenantId\ndevelopment-naos--operations--logging--azureLogAnalytics--authenticationId\ndevelopment-naos--operations--logging--azureLogAnalytics--resourceGroupName\ndevelopment-naos--operations--logging--azureLogAnalytics--subscriptionId\ndevelopment-naos--operations--logging--azureLogAnalytics--workspaceId\ndevelopment-naos--operations--logging--azureLogAnalytics--workspaceName\n```\n\n### Tracing\n```\n           (scoped)                         (scoped)                  (scoped)\n       ┌─────────┐                  ┌─────────────┐             ┌──────────┐\n       │ Tracer  │                  │ ScopeManager│             │ Mediator │\n       └─────────┘  ┌──────────── * └─────────────┘             └──────────┘\n            │       │ SpanBuilder │        │                          │\n            │       └─────────────┘        │                          │\n            │       create │               │                          │\n            x-------------\u003e│               │                          │\n            │      withtag │               │                          │\n            x-------------\u003e│               │                          │\n            │              x─┐             │                          │\n            │              │ │build()      │                          │\n            │              │\u003c┘             │        ┌────── *         │\n            │              | activate(span)│        │ Scope │         │\n            |              x--------------\u003e│        └───────┘         │\n            │              │               x-----------\u003e│             │\n            │              │               │     create │             │\n            │        scope │               │\u003c-----------x             │\n            │\u003c-----------------------------x            │             │\n            │              │               │            │             │\n DISPOSE   ...            ...             ...          ...            │\n   scope    │              │               │            │             │\n            │              │               │\u003c-----------x             │\n            │              │               │  deactivate              │\n            │              │               │                          │\n            │              │               x-------------------------\u003e│\n            │              │               │      publish             │─┐----\u003e handler\n            │              │               │      SpanEndedDomainEvent│ │----\u003e handler\n                                                                      │\u003c┘\n* = newly created, no shared state\n```\n### Journal\n### Dashboard\n\n### operations setup (Azure Log Analytics)\n\n- logging setup: workspaceId, authenticationKey\n- apiAuthentication identity: https://dev.loganalytics.io/documentation/1-Tutorials/ARM-API\n\n# JobScheduling\n\n- Jobs\n- REST Api\n- Registrations\n\t- Handle by an anonymous function\n\t```\n\t.Register(\"anonymousjob2\", Cron.Minutely(), (j) =\u003e System.Diagnostics.Trace.WriteLine(\"+++ hello from task \" + j))\n\t```\n\t- Handle by a type based function\n\t```\n\t.Register\u003cEchoJob\u003e(\"testjob1\", Cron.Minutely(), (j) =\u003e j.EchoAsync(\"+++ hello from testjob1 +++\", CancellationToken.None))\n\t```\n\t- Handle by sending events which a single handler can pick up (Mediatr)\n\t```\n\t.Register(\"jobrequest1\", Cron.Minutely(), () =\u003e new EchoJobEventData { Message = \"+++ hello from jobrequest1 +++\" })\n\t```\n\twill be handled by 'EchoJobEventHandler'\n\n- Schedules\n  - Cron\n\n# FileStorage\n\n- Serializers (json/bson/messagepack)\n- Providers:\n  - Folder\n  - Azure Blob Storage\n  - Azure FileShare Storage\n  - InMemory\n  - SSH\n  - Embedded (resources)\n\n# KeyValueStorage\n\n- FileStorage\n- Azure Table Storage\n- CosmosDb (Table)\n\n# Authentication\n- Basic\n- ApiKey\n- EasyAuth\n\n  Provides support for filling up the request ClaimsPrincipal for an Azure webapp that has been setup to use [EasyAuth](https://docs.microsoft.com/en-us/azure/app-service/overview-authentication-authorization?WT.mc_id=easyauth-github-marouill).\n  ```\n  services.AddNaos(this.Configuration, \"Product\", \"Capability\", new[] { \"All\" }, n =\u003e n\n          ...\n          .AddEasyAuthentication(o =\u003e o.Provider = EasyAuthProviders.AzureActiveDirectory)\n  ```\n  Registers an AuthenticationHandler that will interpret the X-MS-CLIENT-PRINCIPAL-IDP and X-MS-CLIENT-PRINCIPAL HTTP headers that are sent by EasyAuth once a user is logged in.\n  If no user is logged in a PolicyEvaluator will redirect to the appropriate login page.\n\n  ```\n                             .--Naos-------------Azure-----.\n   R      .---Azure---.       |  Easy     |       App       |\n   e ----\u003e| EasyAuth  |-----\u003e |  Auth     |       Service   |\n   q      `-----------`       |  Handler  |     ^           |\n   u            |             `-----x-----------|-----------`\n   e            |                   |           |  \n   s            |                   `-----------`\n   t            V                      provide \n            redirect to                claimsprincipal\n            auth provider (login)\n  ``` \n# (Workflow)?\n\n# Foundation\n- Domain\n  - Model\n  - Repository\n  - Specifications\n- Serialization\n- Web\n  - Client\n  - Filters\n  - Streaming\n  - StartupTasks\n```\n.-----------.    .-----------.       .----------------.             .-----------.   .-----------.\n| Program   |    | WebHost   |       | StartupTask    |             | MyTask    |   | Kester    |\n`-----------`    `-----------`       | ServerDecorator|             `-----------`   | Server    |\n      |               |              `----------------`                 |           `-----------`\n      |  RunAsync()   |                     |  [decorator]              |                |  [decoratee]\n      |--------------\u003e|=--.                 |                           |                |\n      |               |=  | Build           |                           |                |\n      |               |=  | App             |                           |                |\n      |               |=\u003c-\"                 |                           |                |\n      |               |   StartAsync()      |                           |                |\n      |               |--------------------\u003e|   StartAsync()            |                |\n      |               |                     |--------------------------\u003e|  StartAsync()  |\n      |               |                     |                           |---------------\u003e|\n      |               |                     |                           |                |\n```\n- Mapping\n- Criteria\n- Console\n  - Interactive\n  - Commands\n\n\n[![](https://codescene.io/projects/5888/status.svg) Get more details at **codescene.io**.](https://codescene.io/projects/5888/jobs/latest-successful/results)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvip32%2Fnaos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvip32%2Fnaos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvip32%2Fnaos/lists"}