mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-28 01:36:46 +00:00
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 <175728472+Copilot@users.noreply.github.com> Co-authored-by: Iraq Jaber <iraq+github@zitadel.com> Co-authored-by: Iraq <66622793+kkrime@users.noreply.github.com> Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
175 lines
4.3 KiB
Go
175 lines
4.3 KiB
Go
package database
|
|
|
|
type QueryOption func(opts *QueryOpts)
|
|
|
|
// WithCondition sets the condition for the query.
|
|
func WithCondition(condition Condition) QueryOption {
|
|
return func(opts *QueryOpts) {
|
|
opts.Condition = condition
|
|
}
|
|
}
|
|
|
|
// WithOrderBy sets the columns to order the results by.
|
|
func WithOrderBy(ordering OrderDirection, orderBy ...Column) QueryOption {
|
|
return func(opts *QueryOpts) {
|
|
opts.OrderBy = orderBy
|
|
opts.Ordering = ordering
|
|
}
|
|
}
|
|
|
|
func WithOrderByAscending(columns ...Column) QueryOption {
|
|
return WithOrderBy(OrderDirectionAsc, columns...)
|
|
}
|
|
|
|
func WithOrderByDescending(columns ...Column) QueryOption {
|
|
return WithOrderBy(OrderDirectionDesc, columns...)
|
|
}
|
|
|
|
// WithLimit sets the maximum number of results to return.
|
|
func WithLimit(limit uint32) QueryOption {
|
|
return func(opts *QueryOpts) {
|
|
opts.Limit = limit
|
|
}
|
|
}
|
|
|
|
// WithOffset sets the number of results to skip before returning the results.
|
|
func WithOffset(offset uint32) QueryOption {
|
|
return func(opts *QueryOpts) {
|
|
opts.Offset = offset
|
|
}
|
|
}
|
|
|
|
// WithGroupBy sets the columns to group the results by.
|
|
func WithGroupBy(groupBy ...Column) QueryOption {
|
|
return func(opts *QueryOpts) {
|
|
opts.GroupBy = groupBy
|
|
}
|
|
}
|
|
|
|
// WithLeftJoin adds a LEFT JOIN to the query.
|
|
func WithLeftJoin(table string, columns Condition) QueryOption {
|
|
return func(opts *QueryOpts) {
|
|
opts.Joins = append(opts.Joins, join{
|
|
table: table,
|
|
typ: JoinTypeLeft,
|
|
columns: columns,
|
|
})
|
|
}
|
|
}
|
|
|
|
type joinType string
|
|
|
|
const (
|
|
JoinTypeLeft joinType = "LEFT"
|
|
)
|
|
|
|
type join struct {
|
|
table string
|
|
typ joinType
|
|
columns Condition
|
|
}
|
|
|
|
type OrderDirection uint8
|
|
|
|
const (
|
|
OrderDirectionAsc OrderDirection = iota
|
|
OrderDirectionDesc
|
|
)
|
|
|
|
// QueryOpts holds the options for a query.
|
|
// It is used to build the SQL SELECT statement.
|
|
type QueryOpts struct {
|
|
// Condition is the condition to filter the results.
|
|
// It is used to build the WHERE clause of the SQL statement.
|
|
Condition Condition
|
|
// OrderBy is the columns to order the results by.
|
|
// It is used to build the ORDER BY clause of the SQL statement.
|
|
OrderBy Columns
|
|
// Ordering defines if the columns should be ordered ascending or descending.
|
|
// Default is ascending.
|
|
Ordering OrderDirection
|
|
// Limit is the maximum number of results to return.
|
|
// It is used to build the LIMIT clause of the SQL statement.
|
|
Limit uint32
|
|
// Offset is the number of results to skip before returning the results.
|
|
// It is used to build the OFFSET clause of the SQL statement.
|
|
Offset uint32
|
|
// GroupBy is the columns to group the results by.
|
|
// It is used to build the GROUP BY clause of the SQL statement.
|
|
GroupBy Columns
|
|
// Joins is a list of joins to be applied to the query.
|
|
// It is used to build the JOIN clauses of the SQL statement.
|
|
Joins []join
|
|
}
|
|
|
|
func (opts *QueryOpts) Write(builder *StatementBuilder) {
|
|
opts.WriteLeftJoins(builder)
|
|
opts.WriteCondition(builder)
|
|
opts.WriteGroupBy(builder)
|
|
opts.WriteOrderBy(builder)
|
|
opts.WriteLimit(builder)
|
|
opts.WriteOffset(builder)
|
|
}
|
|
|
|
func (opts *QueryOpts) WriteCondition(builder *StatementBuilder) {
|
|
if opts.Condition == nil {
|
|
return
|
|
}
|
|
builder.WriteString(" WHERE ")
|
|
opts.Condition.Write(builder)
|
|
}
|
|
|
|
func (opts *QueryOpts) WriteOrderBy(builder *StatementBuilder) {
|
|
if len(opts.OrderBy) == 0 {
|
|
return
|
|
}
|
|
builder.WriteString(" ORDER BY ")
|
|
for i, col := range opts.OrderBy {
|
|
if i > 0 {
|
|
builder.WriteString(", ")
|
|
}
|
|
col.WriteQualified(builder)
|
|
if opts.Ordering == OrderDirectionDesc {
|
|
builder.WriteString(" DESC")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (opts *QueryOpts) WriteLimit(builder *StatementBuilder) {
|
|
if opts.Limit == 0 {
|
|
return
|
|
}
|
|
builder.WriteString(" LIMIT ")
|
|
builder.WriteArg(opts.Limit)
|
|
}
|
|
|
|
func (opts *QueryOpts) WriteOffset(builder *StatementBuilder) {
|
|
if opts.Offset == 0 {
|
|
return
|
|
}
|
|
builder.WriteString(" OFFSET ")
|
|
builder.WriteArg(opts.Offset)
|
|
}
|
|
|
|
func (opts *QueryOpts) WriteGroupBy(builder *StatementBuilder) {
|
|
if len(opts.GroupBy) == 0 {
|
|
return
|
|
}
|
|
builder.WriteString(" GROUP BY ")
|
|
opts.GroupBy.WriteQualified(builder)
|
|
}
|
|
|
|
func (opts *QueryOpts) WriteLeftJoins(builder *StatementBuilder) {
|
|
if len(opts.Joins) == 0 {
|
|
return
|
|
}
|
|
for _, join := range opts.Joins {
|
|
builder.WriteString(" ")
|
|
builder.WriteString(string(join.typ))
|
|
builder.WriteString(" JOIN ")
|
|
builder.WriteString(join.table)
|
|
builder.WriteString(" ON ")
|
|
join.columns.Write(builder)
|
|
}
|
|
}
|