Skip to content

Commit

Permalink
Added the AzureSqlServerStore adapter. #4
Browse files Browse the repository at this point in the history
  • Loading branch information
jezzsantos committed Aug 18, 2024
1 parent 29f4bcd commit 29443dc
Show file tree
Hide file tree
Showing 15 changed files with 1,674 additions and 8 deletions.
6 changes: 3 additions & 3 deletions README_DERIVATIVE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Repeat for these directories:

You only need the tools below installed if you are going to run specific `Integration.Persistence` tests, for the persistence technology adapters you need to use in your codebase.

* If using the `SqlServerDataStore`, install `SQL Server 2019 Developer`. Available for [download here](https://go.microsoft.com/fwlink/?linkid=866662)
* If using the `AzureSqlServerStore`, install `SQL Server 2019 Developer`. Available for [download here](https://go.microsoft.com/fwlink/?linkid=866662)
* If using the `RedisDataStore`, install `Redis Server` locally to run the tests. Available for [download here](https://redis.io/download)
* If using the `EventStoreEventStore`, install `EventStore` locally to run the tests. Install using the Chocolatey command: `choco install eventstore-oss`

Expand Down Expand Up @@ -187,7 +187,7 @@ In production builds, we build and deploy the code in the `ReleaseForDeploy` bui
>
> We absolutely need to do this because these specific testing code pieces should never exist in the production codebase and may expose security vulnerabilities and exposures we simply don't want in production environments.

The various 3rd party *adapters* we need in production (e.g., `SendGridHttpServiceClient` and the `SqlServerDataStore`)
The various 3rd party *adapters* we need in production (e.g., `SendGridHttpServiceClient` and the `AzureSqlServerStore`)
will be configured in the DI containers (of `Program.cs` of the `ApiHost1` project, and in the modules of each subdomain) to use code to talk to real 3rd party APIs and will be configured with specific production settings in the `appsettings.json` file (overwritten by your CD server).

These are the real 3rd party public API adapters, which, if used with production settings, in local CI environments, or in automated testing environments, may incur financial service charges, trigger rate-limiting quotas, and/or pollute or corrupt real customer data!
Expand All @@ -196,7 +196,7 @@ These are the real 3rd party public API adapters, which, if used with production

You will notice that in the production build (`ReleaseForDeploy`), we have configured the code:

* By injecting the `SqlServerDataStore` as the primary `IDataStore`.
* By injecting the `AzureSqlServerStore` as the primary `IDataStore`.
* By injecting various other dependencies according to the current value of the `$(HostingPlatform)` MS build property (e.g., `HOSTEDONAZURE`).

> Note: that many of the other technology adapters (e.g., `SendGridHttpServiceClient`) will not need to be explicitly configured in the DI container (for specific build flavors), that is because these adapters can be configured to point to local stubs instead of pointing to production environments.
Expand Down
8 changes: 5 additions & 3 deletions docs/design-principles/0130-multitenancy.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ In multi-tenanted SaaS systems, we always share computing resources across multi

However, sometimes we need (due to the our business model) to separate/segregate customer data to different locations or different ownership, e.g., different databases in different global data centers, or a dedicated databases per tenant.

To do that, each tenant requires its own unique configuration for connecting to those services (e.g., the technology-specific `IDataStore` adapter, like the `SqlServerDataStore`, or the `DynamoDbDataStore`).
To do that, each tenant requires its own unique configuration for connecting to those services (e.g., the technology-specific `IDataStore` adapter, like the `AzureSqlServerStore`, or the `DynamoDbDataStore`).

> SaaStack uses a ports and adapters architecture where the adapters have all the knowledge (and read their own configuration) for connecting to remote 3rd party services. This is true for data storage adapters in the same way it is true for any 3rd party adapters to any external service.
Expand All @@ -149,8 +149,10 @@ This needs to happen dynamically at runtime to work effectively, and it works at

1. When an HTTP request comes into the Backend API, a new request "scope" is created for each request by the dependency injection container, so that we can resolve "scoped" dependencies (as well as "transient" dependencies). Note: "singleton" dependencies will have already been resolved at this point in time.
2. The request is processed by the middleware pipeline, and in the middleware pipeline we have the `MultiTenancyMiddleware` that parses the HTTP request and uses the `ITenantDetective` (see detection below) to discover the specific tenant of the inbound request. Once discovered, it sets the `ITenancyContext.Current` with the tenant ID and sets the `ITenancyContext.Settings` for that specific tenant. These settings are fetched from the stored `Organization` that the tenant identifies, which were created when the `Organization` was created.
3. Then, later down the request pipeline, a new instance of an "Application Layer" class (e.g., the `ICarsApplication`), and this application class is injected into the `CarsApi` class, where the request is processed by the remaining code. This application class instance will require a dependency on one or more technology adapters to 3rd party services (e.g., the `ICarRepository`) that will ultimately be dependent on an adapter for the `IDataStore` that can be satisfied by instantiating some technology adapter (e.g., `SqlServerDataStore` or `DynamoDbDataStore`). Into the constructor of this instance of technology adapter, an instance of the `IConfigurationSettings` will be injected, which will ultimately be dependent on the `ITenancyContext.Settings` which, in turn, will fetch specific settings for this specific tenant. (see the `AspNetConfigurationSettings`)
4. Finally, the specific technology adapter (e.g. `SqlServerDataStore` or `DynamoDbDataStore`) will load its configuration settings, the `IConfigurationSettings` will attempt to retrieve them first, from the current `ITenancyContext.Settings` if they exist for the tenant, and if not, then retrieve them from the shared `Platform` settings (available to all tenants).
3. Then, later down the request pipeline, a new instance of an "Application Layer" class (e.g., the `ICarsApplication`), and this application class is injected into the `CarsApi` class, where the request is processed by the remaining code. This application class instance will require a dependency on one or more technology adapters to 3rd party services (e.g., the
`ICarRepository`) that will ultimately be dependent on an adapter for the `IDataStore` that can be satisfied by instantiating some technology adapter (e.g., `AzureSqlServerStore` or `DynamoDbDataStore`). Into the constructor of this instance of technology adapter, an instance of the `IConfigurationSettings` will be injected, which will ultimately be dependent on the
`ITenancyContext.Settings` which, in turn, will fetch specific settings for this specific tenant. (see the `AspNetConfigurationSettings`)
4. Finally, the specific technology adapter (e.g. `AzureSqlServerStore` or `DynamoDbDataStore`) will load its configuration settings, the `IConfigurationSettings` will attempt to retrieve them first, from the current `ITenancyContext.Settings` if they exist for the tenant, and if not, then retrieve them from the shared `Platform` settings (available to all tenants).

Ultimately, the actual settings that are used in any adapter in any Application or Domain Layer, is down to two things:

Expand Down
5 changes: 5 additions & 0 deletions src/ApiHost1/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
"Persistence": {
"LocalMachineJsonFileStore": {
"RootPath": "./saastack/local"
},
"SqlServer": {
"DbServerName": "(local)",
"DbCredentials": "",
"DbName": ""
}
},
"EmailNotifications": {
Expand Down
Loading

0 comments on commit 29443dc

Please sign in to comment.