Primitive Input Types for Business Logic Entry Points
Goal
The fields of command and query types accepted by business-logic entry points must use only primitive or basic types.
A business-logic entry point receives raw input data and is responsible for constructing, loading, or looking up domain entities internally. The caller must never be required to build a domain entity before calling the entry point.
Do not use domain-entity types, domain aggregate types, value objects with behavior, or other complex domain objects as fields in command or query types.
What Counts as In Scope
Apply this skill to code that does one or more of these things:
- defines a
...Command or ...Query type for a business-logic entry point
- defines the fields of an entry-point input type
- passes a domain entity or domain aggregate as a field of a command or query type
- uses a complex domain object where a primitive or basic type would suffice
Allowed Input Types
The following types are allowed as fields in command and query types:
- Strings:
string, str, String
- Numbers:
number, int, float, Int, Long, Double
- Booleans:
boolean, bool, Boolean
- Collections of the above: arrays or lists of any of the allowed types (e.g.,
string[], list[str])
- Nested plain data structures: types composed exclusively of the allowed types above, used to group related input fields (e.g., an
AddressInput with street: string, city: string, zipCode: string)
Forbidden Input Types
The following types must not appear as fields in command or query types:
- Domain entities (e.g.,
Customer, Order, Subscription)
- Domain aggregates
- Value objects that encapsulate behavior or enforce invariants
- Branded or opaque primitive types (e.g.,
CustomerId, OrderId, RequesterId, Email) — use the underlying primitive instead (e.g., string)
- Dates and temporal types (e.g.,
Date, ZonedDateTime, Instant, LocalDate, datetime) — use string in ISO 8601 format instead
- Enums (e.g.,
CarClass, OrderStatus) — use string instead
- Persistence-layer types (e.g., ORM entities, database records)
- Any type that requires the caller to construct a domain object before calling the entry point
The Rule
Every field in a command or query type must be a primitive or basic type.
- Use
string, not Customer or CustomerId.
- Use
string, not OrderStatus or CarClass — represent enum values as strings.
- Use
string in ISO 8601 format, not ZonedDateTime, Instant, or Date.
- Use
OrderLineDraft[] (a plain data structure of primitives) not OrderLine[] if OrderLine is a domain entity.
The entry point is responsible for constructing or loading domain entities.
- The entry point receives primitive input, then creates new domain entities or loads existing ones from a repository.
- The caller does not need to know how domain entities are structured or instantiated.
Nested input structures must also contain only allowed types.
- If a command field is a nested object, every field in that nested object must also be a primitive or basic type.
- Do not smuggle domain entities or branded types inside nested input structures.
Detection Workflow
Find command and query types.
- Identify
...Command and ...Query types used by business-logic entry points.
Inspect each field.
- Check whether the field type is a string, number, boolean, or a collection of these.
- Flag any field whose type is a domain entity, domain aggregate, value object, branded/opaque primitive type, enum, or temporal type.
Check nested structures.
- If a field is a nested object type, verify that all of its fields are also allowed types.
Trace the domain entity construction.
- Verify that domain entities are constructed or loaded inside the entry point, not received as input.
Writing or Changing Input Types
Start from the data the caller provides.
- Identify what raw data the caller has: IDs as strings, numbers, booleans, dates as ISO 8601 strings, enum values as strings.
- Express each piece of input data as a string, number, or boolean.
Use raw primitives for all values.
- Use
string for entity IDs, email addresses, dates (ISO 8601), enum values, and other values that may have domain-specific types in the domain layer.
- Use
number for quantities, amounts, and numeric values.
- Use
boolean for flags and binary choices.
- The entry point is the boundary where raw primitives enter and domain types begin.
Use plain data structures for grouped input.
- When multiple related values travel together (e.g., address fields), define a plain input structure with only allowed types.
- Name these structures to reflect their input purpose (e.g.,
AddressInput, OrderLineDraft).
Let the entry point build domain objects.
- Construct new domain entities from the primitive input inside the entry point.
- Load existing domain entities from repositories using the provided IDs.
Examples
Use this:
type CreateOrderCommand = {
requesterId: string
customerId: string
lines: OrderLineDraft[]
}
type OrderLineDraft = {
productId: string
quantity: number
}
Not this:
type CreateOrderCommand = {
requesterId: RequesterId
customerId: CustomerId
lines: OrderLine[]
}
Use this:
@dataclass(frozen=True)
class CreateOrderCommand:
requester_id: str
customer_id: str
lines: list[OrderLineDraft]
@dataclass(frozen=True)
class OrderLineDraft:
product_id: str
quantity: int
Not this:
@dataclass(frozen=True)
class CreateOrderCommand:
requester_id: RequesterId
customer_id: CustomerId
lines: list[OrderLine]
Use this:
data class CreateOrderCommand(
val requesterId: String,
val customerId: String,
val lines: List<OrderLineDraft>,
)
data class OrderLineDraft(
val productId: String,
val quantity: Int,
)
Not this:
data class CreateOrderCommand(
val requesterId: RequesterId,
val customerId: CustomerId,
val lines: List<OrderLine>,
)
Review Questions
When reading or reviewing code, ask:
- Are all fields in this command or query type strings, numbers, booleans, or collections of these?
- Does any field use a domain-entity type, domain aggregate, value object, branded/opaque primitive type, enum, or temporal type?
- Are nested input structures composed exclusively of allowed types?
- Is the entry point responsible for constructing or loading domain entities from the primitive input?
- Does the caller need to build a domain entity before calling this entry point?
If any input field uses a forbidden type, apply this skill.
Report the Outcome
When finishing the task:
- state which command or query types were identified or changed
- state which fields were changed from domain or branded types to raw primitive types
- state which nested input structures were introduced or corrected
- state how domain-entity construction was moved inside the entry point
1---2name: business-logic-entry-point-primitive-input-types3description: Require business-logic entry point input parameters to use only primitive or basic types. Use when an agent needs to create, modify, review, or interpret the command or query types accepted by business-logic entry points. Input fields must be strings, numbers, or booleans. Do not use domain-entity types, domain aggregate types, branded or opaque primitive types, enums, temporal types, or other complex domain objects as input fields.4---56# Primitive Input Types for Business Logic Entry Points78## Goal910The fields of command and query types accepted by business-logic entry points must use only primitive or basic types.1112A business-logic entry point receives raw input data and is responsible for constructing, loading, or looking up domain entities internally. The caller must never be required to build a domain entity before calling the entry point.1314Do not use domain-entity types, domain aggregate types, value objects with behavior, or other complex domain objects as fields in command or query types.1516## What Counts as In Scope1718Apply this skill to code that does one or more of these things:1920- defines a `...Command` or `...Query` type for a business-logic entry point21- defines the fields of an entry-point input type22- passes a domain entity or domain aggregate as a field of a command or query type23- uses a complex domain object where a primitive or basic type would suffice2425## Allowed Input Types2627The following types are allowed as fields in command and query types:2829- **Strings**: `string`, `str`, `String`30- **Numbers**: `number`, `int`, `float`, `Int`, `Long`, `Double`31- **Booleans**: `boolean`, `bool`, `Boolean`32- **Collections of the above**: arrays or lists of any of the allowed types (e.g., `string[]`, `list[str]`)33- **Nested plain data structures**: types composed exclusively of the allowed types above, used to group related input fields (e.g., an `AddressInput` with `street: string`, `city: string`, `zipCode: string`)3435## Forbidden Input Types3637The following types must not appear as fields in command or query types:3839- Domain entities (e.g., `Customer`, `Order`, `Subscription`)40- Domain aggregates41- Value objects that encapsulate behavior or enforce invariants42- Branded or opaque primitive types (e.g., `CustomerId`, `OrderId`, `RequesterId`, `Email`) — use the underlying primitive instead (e.g., `string`)43- Dates and temporal types (e.g., `Date`, `ZonedDateTime`, `Instant`, `LocalDate`, `datetime`) — use `string` in ISO 8601 format instead44- Enums (e.g., `CarClass`, `OrderStatus`) — use `string` instead45- Persistence-layer types (e.g., ORM entities, database records)46- Any type that requires the caller to construct a domain object before calling the entry point4748## The Rule49501. Every field in a command or query type must be a primitive or basic type.51 - Use `string`, not `Customer` or `CustomerId`.52 - Use `string`, not `OrderStatus` or `CarClass` — represent enum values as strings.53 - Use `string` in ISO 8601 format, not `ZonedDateTime`, `Instant`, or `Date`.54 - Use `OrderLineDraft[]` (a plain data structure of primitives) not `OrderLine[]` if `OrderLine` is a domain entity.55562. The entry point is responsible for constructing or loading domain entities.57 - The entry point receives primitive input, then creates new domain entities or loads existing ones from a repository.58 - The caller does not need to know how domain entities are structured or instantiated.59603. Nested input structures must also contain only allowed types.61 - If a command field is a nested object, every field in that nested object must also be a primitive or basic type.62 - Do not smuggle domain entities or branded types inside nested input structures.6364## Detection Workflow65661. Find command and query types.67 - Identify `...Command` and `...Query` types used by business-logic entry points.68692. Inspect each field.70 - Check whether the field type is a string, number, boolean, or a collection of these.71 - Flag any field whose type is a domain entity, domain aggregate, value object, branded/opaque primitive type, enum, or temporal type.72733. Check nested structures.74 - If a field is a nested object type, verify that all of its fields are also allowed types.75764. Trace the domain entity construction.77 - Verify that domain entities are constructed or loaded inside the entry point, not received as input.7879## Writing or Changing Input Types80811. Start from the data the caller provides.82 - Identify what raw data the caller has: IDs as strings, numbers, booleans, dates as ISO 8601 strings, enum values as strings.83 - Express each piece of input data as a string, number, or boolean.84852. Use raw primitives for all values.86 - Use `string` for entity IDs, email addresses, dates (ISO 8601), enum values, and other values that may have domain-specific types in the domain layer.87 - Use `number` for quantities, amounts, and numeric values.88 - Use `boolean` for flags and binary choices.89 - The entry point is the boundary where raw primitives enter and domain types begin.90913. Use plain data structures for grouped input.92 - When multiple related values travel together (e.g., address fields), define a plain input structure with only allowed types.93 - Name these structures to reflect their input purpose (e.g., `AddressInput`, `OrderLineDraft`).94954. Let the entry point build domain objects.96 - Construct new domain entities from the primitive input inside the entry point.97 - Load existing domain entities from repositories using the provided IDs.9899## Examples100101Use this:102103```ts104type CreateOrderCommand = {105 requesterId: string106 customerId: string107 lines: OrderLineDraft[]108}109110type OrderLineDraft = {111 productId: string112 quantity: number113}114```115116Not this:117118```ts119type CreateOrderCommand = {120 requesterId: RequesterId121 customerId: CustomerId122 lines: OrderLine[]123}124```125126Use this:127128```py129@dataclass(frozen=True)130class CreateOrderCommand:131 requester_id: str132 customer_id: str133 lines: list[OrderLineDraft]134135@dataclass(frozen=True)136class OrderLineDraft:137 product_id: str138 quantity: int139```140141Not this:142143```py144@dataclass(frozen=True)145class CreateOrderCommand:146 requester_id: RequesterId147 customer_id: CustomerId148 lines: list[OrderLine]149```150151Use this:152153```kt154data class CreateOrderCommand(155 val requesterId: String,156 val customerId: String,157 val lines: List<OrderLineDraft>,158)159160data class OrderLineDraft(161 val productId: String,162 val quantity: Int,163)164```165166Not this:167168```kt169data class CreateOrderCommand(170 val requesterId: RequesterId,171 val customerId: CustomerId,172 val lines: List<OrderLine>,173)174```175176## Review Questions177178When reading or reviewing code, ask:179180- Are all fields in this command or query type strings, numbers, booleans, or collections of these?181- Does any field use a domain-entity type, domain aggregate, value object, branded/opaque primitive type, enum, or temporal type?182- Are nested input structures composed exclusively of allowed types?183- Is the entry point responsible for constructing or loading domain entities from the primitive input?184- Does the caller need to build a domain entity before calling this entry point?185186If any input field uses a forbidden type, apply this skill.187188## Report the Outcome189190When finishing the task:191192- state which command or query types were identified or changed193- state which fields were changed from domain or branded types to raw primitive types194- state which nested input structures were introduced or corrected195- state how domain-entity construction was moved inside the entry point