The concept of domain driven design (DDD) already exists in REST api. For example, when we develop an express server, we differentiate between routes (application layer), handlers (domain layer) and clients (infrastructure layer). As well, micro-service could follow the same guideline.
We would like isolate dynamic business logics from the "static" parts (method definition, how to interact with db).
As well, we would like to isolate business logics by domain. For example, 'Author' related logic and 'Book' related logic should not be written in the same file.
In conclusion, with DDD, we can concentrate on the dynamic functional logic (business logic) by domain. Domain logic is the key to functional features (and might not be very possiblily replaced by AI because as human we need to finger out all the scenarios :D ).
For DDD in a micro-service, there should be:
The calling order should be from top to bottom (application layer calls domain layer, domain layer calls infrastructure layer), not the reversed way.
However, when we really develope, I propose to develop from bottom to the top (first infrastructure layer, then domain layer) so we can test the business logic directly.
Inside application layer, we define:
Define schema and infer type:
import { z } from "zod"
// Define a Zod schema
const inputSchema = z.object({
id: z.number(),
name: z.string(),
})
// generate type from zod schema, to be reused in domain layer
type InputType = z.infer<typeof inputSchema>
Type validation:
try {
// Validate the input data
inputSchema.parse(inputData)
} catch (error) {
throw new Error("validation fails") // server should throw 400 error
}
In infrastructure layer, we define:
Domain layer will call infra layer to return results or throw error for Application layer. Sometimes it's only pass through, but for real it is more complicated, because we need to think of all the scenarios and design related errors.
For example, if we want to update an entity, besides the success scenario, we need to define and throw errors for the following cases:
Each failure scenario should match a customized error. This error will be thrown from micro-service to api side, then api will parse those errors and throw a proper API / Graphql Error to the client side.
Thanks for reading !