-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e760b93
commit 514fdb3
Showing
93 changed files
with
3,952 additions
and
435 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# User Lifecycle | ||
|
||
## Design Principles | ||
|
||
* We want to use the email address as a unique identifier for each person (in the universe), and we only want one representation of that person in all organizations (tenants). However, a person can have several identities (a specific company identity versus a personal identity). | ||
* We want any registered user in the system to invite another person (via an email address) to register themselves to the system, to capture the email address early. | ||
* Only the invited person can self-register after they were "warm" invited, or they approached the platform "cold". | ||
* When registering, regardless of whether they were invited via an existing email address or not, they can choose the email address they wish to register with. | ||
* | ||
|
||
## Implementation | ||
|
||
Users are managed in the `EndUser` subdomain. | ||
|
||
Users cannot be deleted. Once registered, they can be `disabled` and suspended from using the product. | ||
|
||
Once registered, the email address they used to register will be used as their username. Even though they may change that email address, no two users on the platform can use the same email address. | ||
|
||
Users can self-register from the `Identity` subdomain via either `PasswordCredentials`, `SSOUsers`, or other methods, each responsible for its own onboarding flow (e.g., passwords require email confirmations and a managed registration flow, whereas SSO can just automate it). | ||
|
||
New users can be invited by another party (authenticated or not) and can respond to that invitation or register without an invite. | ||
|
||
![User Lifecycle](../images/EndUser-Lifecycle.png) | ||
|
||
### Organizations | ||
|
||
An `Organization` has a type of either `Personal` or `Shared`. | ||
|
||
* `Shared` organizations are intended for use by companies/workgroups/organizations/teams/etc and for persons and machines. | ||
* `Personal` organizations are for each `EndUser` (person or machine) to use on the platform and cannot be shared with others. | ||
* A person/machine will have a membership to one `Personal` organization at all times, and can have a membership to one or more `Shared` organizations. | ||
|
||
#### Roles and Responsibilities | ||
|
||
* Any person can have the `Member` and/or `Owner` and/or `BillingAdmin` roles in an organization. | ||
* Only `Owner` roles can assign/unassign roles to other members. | ||
* Any person can have the `BillingAdmin` role of an organization, but they must also have the `Owner` role. | ||
* Every organization (`Shared` or `Personal`) will always have a person who is the "billing subscriber". This person has a fiscal responsibility to pay for billing charges for that organization (tenant). The "billing subscriber" must always have the `Owner` and `BillingAdmin` roles at all times. | ||
* When a person creates a new organization, they automatically become an `Owner` and `BillingAdmin` of it, as well as the "billing subscriber" for it. | ||
* From that point, they can assign the `Owner` and `BillingAdmin` roles to one or more other members of the organization. | ||
* The "billing subscriber" responsibility must be transferred via a (voluntary) payment method submission from another person with the `BillingAdmin` (role). | ||
|
||
#### Personal Organizations | ||
|
||
* Every `EndUser` (person or machine) has one `Personal` organization. | ||
|
||
* It is automatically created for them when they register on the platform. It is named after that person/machine. | ||
* That person/machine is the only member of that organization. | ||
|
||
* They have the roles of `Owner` and `BillingAdmin`, and they are also the "billing subscriber" for it. | ||
|
||
* This organization cannot be deleted, and that person cannot be removed from it, nor can their roles be changed. | ||
|
||
> This organization is very important so that the product can be accessed at all times by them, regardless of whether the owner is a member of any `Shared` organizations or not. | ||
#### Shared Organizations | ||
|
||
* `Shared` organizations can be created at any time by any person on the platform (not machines). | ||
* Any other person/machine can be invited into them. When they are, they are created a `Membership` to that `Organization`, and each `Membership` maintains its own roles. | ||
* A person (or machine) can be a `Member` (role) of any number of `Shared` organizations into which they can be invited, removed, or they can leave themselves. | ||
* A person (not a machine) can be assigned/unassigned any number of roles in those other organizations. | ||
* A `Shared` organization must have at least one `Owner` (role) and one `BillingAdmin` (role) at all times, and they can be the same person or different persons. Like a `Personal` organization, a `Shared` will have one and only one "billing subscriber", who is ultimately responsible for any charges for the organization. | ||
* A `Shared` organization can be deleted. However, they have to be deleted by the designated "billing subscriber" and only once all members are removed from it. | ||
|
||
### Invitations | ||
|
||
TBD how do they work? | ||
|
||
Memberships etc. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,20 @@ | ||
# Design Principles | ||
|
||
[All Use Cases](0150-all-use-cases.md) the main use cases that we have implemented across the product (so that you do not have to implement them yourselves) | ||
|
||
* [REST API Design Guidelines](0010-rest-api.md) how REST API's should be designed | ||
* [REST API Framework](0020-api-framework.md) how REST API's are implemented | ||
* [Modularity](0025-modularity.md) how we build modules that can be scaled-out later as the product grows | ||
* [Modularity](0025-modularity.md) is how we build modules that can be scaled-out later as the product grows | ||
* [Recording/Logging/etc](0030-recording.md) how we do crash reporting, logging, auditing, and capture usage metrics | ||
* [Configuration Management](0040-configuration.md) how we manage configuration in the source code at design-time and runtime | ||
* [Domain Driven Design](0050-domain-driven-design.md) how to design your aggregates, and domains | ||
* [Dependency Injection](0060-dependency-injection.md) how you implement DI | ||
* [Persistence](0070-persistence.md) how you design your repository layer, and promote domain events | ||
* [Ports and Adapters](0080-ports-and-adapters.md) how we keep infrastructure components at arms length, and testable, and how we integrate with any 3rd party system | ||
* [Ports and Adapters](0080-ports-and-adapters.md) how we keep infrastructure components at arm's length, and testable, and how we integrate with any 3rd party system | ||
* [Authentication and Authorization](0090-authentication-authorization.md) how we authenticate and authorize users | ||
* [Email Delivery](0100-email-delivery.md) how we send emails and deliver them asynchronously and reliably | ||
* [Backend for Frontend](0110-back-end-for-front-end.md) the BEFFE web server that is tailored for a web UI, and brokers secure access to the backend | ||
* [Feature Flagging](0120-feature-flagging.md) how we enable and disable features at runtime | ||
* [Multi-Tenancy](0130-multitenancy.md) how we support multiple tenants in our system (both logically and physical infrastructure) | ||
* [Multi-Tenancy](0130-multitenancy.md) how we support multiple tenants in our system (both logical and physical infrastructure) | ||
* [Developer Tooling](0140-developer-tooling.md) all the tooling that is included in this codebase to help developers use this codebase effectively, and consistently | ||
* [All Use Cases](0150-all-use-cases.md) the main use cases that we have implemented across the product (so you dont have to) | ||
* [User Lifecycle](0160-user-lifecycle.md) how are users managed on the platform, and the relationship to their organizations |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using Common; | ||
|
||
namespace Application.Persistence.Common.Extensions; | ||
|
||
public static class Tasks | ||
{ | ||
/// <summary> | ||
/// Runs all the specified <see cref="tasks" /> and returns the first <see cref="Error" /> if any | ||
/// </summary> | ||
public static async Task<Result<bool, Error>> WhenAllAsync(params Task<Result<bool, Error>>[] tasks) | ||
{ | ||
var results = await Task.WhenAll(tasks); | ||
|
||
var hasError = results.Any(result => !result.IsSuccessful); | ||
if (hasError) | ||
{ | ||
return results.First(result => !result.IsSuccessful).Error; | ||
} | ||
|
||
return results.All(result => result.Value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,7 @@ public void WhenConstructedAndInvalidEmail_ThenReturnsError() | |
[Fact] | ||
public void WhenGuessPersonNameFromEmailAndPlainUsername_ThenReturnsName() | ||
{ | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonName(); | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonFullName(); | ||
|
||
name.FirstName.Text.Should().Be("Auser"); | ||
name.LastName.Should().BeNone(); | ||
|
@@ -36,7 +36,7 @@ public void WhenGuessPersonNameFromEmailAndPlainUsername_ThenReturnsName() | |
[Fact] | ||
public void WhenGuessPersonNameFromEmailAndMultipleDottedUsername_ThenReturnsName() | ||
{ | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonName(); | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonFullName(); | ||
|
||
name.FirstName.Text.Should().Be("Afirstname"); | ||
name.LastName.Value.Text.Should().Be("Alastname"); | ||
|
@@ -45,7 +45,7 @@ public void WhenGuessPersonNameFromEmailAndMultipleDottedUsername_ThenReturnsNam | |
[Fact] | ||
public void WhenGuessPersonNameFromEmailAndTwoDottedUsername_ThenReturnsName() | ||
{ | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonName(); | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonFullName(); | ||
|
||
name.FirstName.Text.Should().Be("Afirstname"); | ||
name.LastName.Value.Text.Should().Be("Alastname"); | ||
|
@@ -54,7 +54,7 @@ public void WhenGuessPersonNameFromEmailAndTwoDottedUsername_ThenReturnsName() | |
[Fact] | ||
public void WhenGuessPersonNameFromEmailAndContainsPlusSign_ThenReturnsName() | ||
{ | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonName(); | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonFullName(); | ||
|
||
name.FirstName.Text.Should().Be("Afirstname"); | ||
name.LastName.Should().BeNone(); | ||
|
@@ -63,7 +63,7 @@ public void WhenGuessPersonNameFromEmailAndContainsPlusSign_ThenReturnsName() | |
[Fact] | ||
public void WhenGuessPersonNameFromEmailAndContainsPlusSignAndNumber_ThenReturnsName() | ||
{ | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonName(); | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonFullName(); | ||
|
||
name.FirstName.Text.Should().Be("Afirstname"); | ||
name.LastName.Should().BeNone(); | ||
|
@@ -72,7 +72,7 @@ public void WhenGuessPersonNameFromEmailAndContainsPlusSignAndNumber_ThenReturns | |
[Fact] | ||
public void WhenGuessPersonNameFromEmailAndGuessedFirstNameNotValid_ThenReturnsNameWithFallbackFirstName() | ||
{ | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonName(); | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonFullName(); | ||
|
||
name.FirstName.Text.Should().Be(Resources.EmailAddress_FallbackGuessedFirstName); | ||
name.LastName.Should().BeNone(); | ||
|
@@ -81,7 +81,7 @@ public void WhenGuessPersonNameFromEmailAndGuessedFirstNameNotValid_ThenReturnsN | |
[Fact] | ||
public void WhenGuessPersonNameFromEmailAndGuessedLastNameNotValid_ThenReturnsNameWithNoLastName() | ||
{ | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonName(); | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonFullName(); | ||
|
||
name.FirstName.Text.Should().Be("Afirstname"); | ||
name.LastName.Should().BeNone(); | ||
|
@@ -90,7 +90,7 @@ public void WhenGuessPersonNameFromEmailAndGuessedLastNameNotValid_ThenReturnsNa | |
[Fact] | ||
public void WhenGuessPersonNameFromEmailAndGuessedFirstAndLastNameNotValid_ThenReturnsNameWithNoLastName() | ||
{ | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonName(); | ||
var name = EmailAddress.Create("[email protected]").Value.GuessPersonFullName(); | ||
|
||
name.FirstName.Text.Should().Be(Resources.EmailAddress_FallbackGuessedFirstName); | ||
name.LastName.Should().BeNone(); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.