Skip to content

Children

Children define nested collections within a projection. Each child represents a collection of items that are managed independently but belong to a parent projection.

children {CollectionName} identified by {Identifier}
{child blocks}
projection Group => GroupReadModel
from GroupCreated
Name = name
children members identified by userId
from UserAddedToGroup key userId
parent groupId
Name = userName
Role = role

This creates a members collection where each member is identified by userId.

The identified by specifies how to identify individual children:

children orders identified by orderId
children members identified by userId
children items identified by itemNumber

Use parent to specify the relationship to the parent projection:

children members identified by userId
from UserAddedToGroup key userId
parent groupId
Name = userName

The parent groupId links the child to the correct parent instance.

Apply AutoMap to children:

children members identified by userId
automap
from UserAddedToGroup
Role = role

Children can have multiple event types:

children members identified by userId
from UserAddedToGroup key userId
parent groupId
Name = userName
Role = role
from UserRoleChanged key userId
parent groupId
Role = role

Apply mappings to all events within a children collection using the every block:

children members identified by userId
from UserAddedToGroup key userId
parent groupId
Role = role
every
Name = userName
UpdatedAt = $eventContext.occurred

The every block within children works similarly to the top-level every block but applies only to events within that specific children collection. It’s useful for mappings that should apply to all events affecting child items.

Note: The exclude children directive is not applicable within child every blocks as they already operate in a children context.

Children can have joins:

children members id userId
from UserAddedToGroup key userId
parent groupId
UserId = userId
Role = role
join User on UserId
events UserCreated, UserUpdated
Name = name
Email = email

Remove children when specific events occur:

children members id userId
from UserAddedToGroup key userId
parent groupId
Role = role
remove with UserRemovedFromGroup key userId
parent groupId

See Removal for more details.

Children can contain their own children:

children departments id deptId
from DepartmentCreated key deptId
parent companyId
Name = name
children teams id teamId
from TeamCreated key teamId
parent deptId
Name = name
projection Group => GroupReadModel
from GroupCreated
Name = name
Description = description
children members id userId
automap
from UserAddedToGroup key userId
parent groupId
AddedAt = $eventContext.occurred
from UserRoleChanged key userId
parent groupId
Role = role
remove with UserRemovedFromGroup key userId
parent groupId
projection Order => OrderReadModel
from OrderPlaced
OrderNumber = orderNumber
CustomerId = customerId
Total = 0
children items id lineNumber
from LineItemAdded key lineNumber
parent orderId
ProductId = productId
Quantity = quantity
UnitPrice = price
LineTotal = total
from LineItemQuantityChanged key lineNumber
parent orderId
Quantity = quantity
LineTotal = total
remove with LineItemRemoved key lineNumber
parent orderId
projection Project => ProjectReadModel
from ProjectCreated
Name = name
Status = "Active"
children tasks id taskId
every
LastModified = $eventContext.occurred
from TaskAdded key taskId
parent projectId
Title = title
AssignedTo = assigneeId
Status = "Open"
CreatedAt = $eventContext.occurred
from TaskCompleted key taskId
parent projectId
Status = "Completed"
CompletedAt = $eventContext.occurred
from TaskAssigned key taskId
parent projectId
AssignedTo = assigneeId
join User on AssignedTo
events UserCreated
AssigneeName = name
projection Invoice => InvoiceReadModel
from InvoiceIssued
InvoiceNumber = number
CustomerId = customerId
Amount = amount
Balance = amount
children payments id paymentId
from PaymentReceived key paymentId
parent invoiceNumber
Amount = amount
ReceivedAt = $eventContext.occurred
Method = paymentMethod
from PaymentReceived
subtract Balance by amount
projection BlogPost => BlogPostReadModel
from PostPublished
Title = title
Content = content
AuthorId = authorId
PublishedAt = $eventContext.occurred
from PostUpdated
Content = content
UpdatedAt = $eventContext.occurred
children comments id commentId
from CommentAdded key commentId
parent postId
AuthorId = authorId
Content = content
CreatedAt = $eventContext.occurred
from CommentEdited key commentId
parent postId
Content = content
EditedAt = $eventContext.occurred
remove with CommentDeleted key commentId
parent postId
join User on AuthorId
events UserRegistered
AuthorName = name
projection Company => CompanyReadModel
from CompanyCreated
Name = name
children departments id deptId
from DepartmentCreated key deptId
parent companyId
Name = name
children teams id teamId
from TeamCreated key teamId
parent deptId
Name = name
children members id memberId
from MemberAdded key memberId
parent teamId
Name = name
Role = role

The parent key can use various expressions:

children items id itemId
from ItemAdded key itemId
parent orderId # Event property
# or
parent $eventContext.eventSourceId # Event source ID
# or
parent order.id # Nested property

AutoMap at the children level applies to all child events:

children members id userId
automap
from UserAdded key userId
parent groupId
# Name, Email, etc. are auto-mapped
from UserUpdated key userId
parent groupId
# Updates are auto-mapped

The children block maps to a collection property on the read model:

public class GroupReadModel
{
public string Name { get; set; }
public List<GroupMember> Members { get; set; } = new();
}
public class GroupMember
{
public string UserId { get; set; }
public string Name { get; set; }
public string Role { get; set; }
}
  1. Clear Identifiers: Use meaningful identifiers that clearly identify children
  2. Parent Keys: Always specify parent keys to maintain relationships
  3. Removal: Include removal logic for children that can be deleted
  4. Joins for Enrichment: Use joins to add related data to children
  5. Nested Carefully: Deep nesting can impact performance and complexity
  6. AutoMap Selectively: Use AutoMap for children when property names align
  7. Consistent Patterns: Use consistent event patterns across children
children items id itemId
from ItemAdded key itemId
parent parentId
# properties
from ItemUpdated key itemId
parent parentId
# updated properties
remove with ItemRemoved key itemId
parent parentId
children members id userId
from MemberAdded key userId
parent groupId
UserId = userId
join User on UserId
events UserCreated, UserUpdated
Name = name
Email = email
```pdl
### Versioning
```pdl
children versions id versionNumber
from VersionCreated key versionNumber
parent documentId
Content = content
CreatedAt = $eventContext.occurred
CreatedBy = authorId
  • Joins - Enriching children with related data
  • Removal - Removing children when events occur
  • Keys - Understanding child and parent keys