Files
zitadel/backend/v3/domain/org_add.go
T
61cab8878e feat(backend): state persisted objects (#9870)
This PR initiates the rework of Zitadel's backend to state-persisted
objects. This change is a step towards a more scalable and maintainable
architecture.

## Changes

* **New `/backend/v3` package**: A new package structure has been
introduced to house the reworked backend logic. This includes:
* `domain`: Contains the core business logic, commands, and repository
interfaces.
* `storage`: Implements the repository interfaces for database
interactions with new transactional tables.
  * `telemetry`: Provides logging and tracing capabilities.
* **Transactional Tables**: New database tables have been defined for
`instances`, `instance_domains`, `organizations`, and `org_domains`.
* **Projections**: New projections have been created to populate the new
relational tables from the existing event store, ensuring data
consistency during the migration.
* **Repositories**: New repositories provide an abstraction layer for
accessing and manipulating the data in the new tables.
* **Setup**: A new setup step for `TransactionalTables` has been added
to manage the database migrations for the new tables.

This PR lays the foundation for future work to fully transition to
state-persisted objects for these components, which will improve
performance and simplify data access patterns.

This PR initiates the rework of ZITADEL's backend to state-persisted
objects. This is a foundational step towards a new architecture that
will improve performance and maintainability.

The following objects are migrated from event-sourced aggregates to
state-persisted objects:

* Instances
  * incl. Domains
* Orgs
  * incl. Domains

The structure of the new backend implementation follows the software
architecture defined in this [wiki
page](https://github.com/zitadel/zitadel/wiki/Software-Architecturel).

This PR includes:

* The initial implementation of the new transactional repositories for
the objects listed above.
* Projections to populate the new relational tables from the existing
event store.
* Adjustments to the build and test process to accommodate the new
backend structure.

This is a work in progress and further changes will be made to complete
the migration.

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Iraq Jaber <[email protected]>
Co-authored-by: Iraq <[email protected]>
Co-authored-by: Tim Möhlmann <[email protected]>
2025-09-05 09:54:34 +01:00

138 lines
3.2 KiB
Go

package domain
// import (
// "context"
// "github.com/zitadel/zitadel/backend/v3/storage/eventstore"
// )
// // AddOrgCommand adds a new organization.
// // I'm unsure if we should add the Admins here or if this should be a separate command.
// type AddOrgCommand struct {
// ID string `json:"id"`
// Name string `json:"name"`
// Admins []*AddMemberCommand `json:"admins"`
// }
// func NewAddOrgCommand(name string, admins ...*AddMemberCommand) *AddOrgCommand {
// return &AddOrgCommand{
// Name: name,
// Admins: admins,
// }
// }
// // String implements [Commander].
// func (cmd *AddOrgCommand) String() string {
// return "AddOrgCommand"
// }
// // Execute implements Commander.
// func (cmd *AddOrgCommand) Execute(ctx context.Context, opts *CommandOpts) (err error) {
// if len(cmd.Admins) == 0 {
// return ErrNoAdminSpecified
// }
// if err = cmd.ensureID(); err != nil {
// return err
// }
// close, err := opts.EnsureTx(ctx)
// if err != nil {
// return err
// }
// defer func() { err = close(ctx, err) }()
// err = orgRepo(opts.DB).Create(ctx, &Org{
// ID: cmd.ID,
// Name: cmd.Name,
// })
// if err != nil {
// return err
// }
// for _, admin := range cmd.Admins {
// admin.orgID = cmd.ID
// if err = opts.Invoke(ctx, admin); err != nil {
// return err
// }
// }
// orgCache.Set(ctx, &Org{
// ID: cmd.ID,
// Name: cmd.Name,
// })
// return nil
// }
// // Events implements [eventer].
// func (cmd *AddOrgCommand) Events() []*eventstore.Event {
// return []*eventstore.Event{
// {
// AggregateType: "org",
// AggregateID: cmd.ID,
// Type: "org.added",
// Payload: cmd,
// },
// }
// }
// var (
// _ Commander = (*AddOrgCommand)(nil)
// _ eventer = (*AddOrgCommand)(nil)
// )
// func (cmd *AddOrgCommand) ensureID() (err error) {
// if cmd.ID != "" {
// return nil
// }
// cmd.ID, err = generateID()
// return err
// }
// // AddMemberCommand adds a new member to an organization.
// // I'm not sure if we should make it more generic to also use it for instances.
// type AddMemberCommand struct {
// orgID string
// UserID string `json:"userId"`
// Roles []string `json:"roles"`
// }
// func NewAddMemberCommand(userID string, roles ...string) *AddMemberCommand {
// return &AddMemberCommand{
// UserID: userID,
// Roles: roles,
// }
// }
// // String implements [Commander].
// func (cmd *AddMemberCommand) String() string {
// return "AddMemberCommand"
// }
// // Execute implements Commander.
// func (a *AddMemberCommand) Execute(ctx context.Context, opts *CommandOpts) (err error) {
// close, err := opts.EnsureTx(ctx)
// if err != nil {
// return err
// }
// defer func() { err = close(ctx, err) }()
// return orgRepo(opts.DB).Member().AddMember(ctx, a.orgID, a.UserID, a.Roles)
// }
// // Events implements [eventer].
// func (a *AddMemberCommand) Events() []*eventstore.Event {
// return []*eventstore.Event{
// {
// AggregateType: "org",
// AggregateID: a.UserID,
// Type: "member.added",
// Payload: a,
// },
// }
// }
// var (
// _ Commander = (*AddMemberCommand)(nil)
// _ eventer = (*AddMemberCommand)(nil)
// )