Skip to content

Commit 7fcd7f5

Browse files
committed
content: posts
1 parent ead5622 commit 7fcd7f5

File tree

3 files changed

+269
-0
lines changed

3 files changed

+269
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# CQRS: Command Query Responsibility Segregation in Modern Architecture
2+
3+
In contemporary software architecture, we often encounter systems where the complexity of data retrieval differs significantly from the complexity of data modification. **CQRS (Command Query Responsibility Segregation)** is a pattern that addresses this asymmetry by using different models for updating and reading information.
4+
5+
As defined by Greg Young and popularized by Martin Fowler, CQRS is fundamentally about separating the "Command" (Write) side from the "Query" (Read) side of an application.
6+
7+
## The Architectural Core
8+
9+
The core premise of CQRS is that any method should be either a **Command**, which performs an action and changes the state of a system but returns no data, or a **Query**, which returns data to the caller but does not change the state.
10+
11+
```mermaid
12+
graph LR
13+
User([User])
14+
subgraph "Application"
15+
CommandBus[Command Bus]
16+
QueryBus[Query Bus]
17+
18+
subgraph "Write Side"
19+
CH[Command Handlers]
20+
WM[(Write Database)]
21+
end
22+
23+
subgraph "Read Side"
24+
QH[Query Handlers]
25+
RM[(Read Database)]
26+
end
27+
end
28+
29+
User -->|Sends Command| CommandBus
30+
CommandBus --> CH
31+
CH --> WM
32+
33+
User -->|Executes Query| QueryBus
34+
QueryBus --> QH
35+
QH --> RM
36+
37+
WM -.->|Sync/Event| RM
38+
```
39+
40+
## Implementation in Go
41+
42+
Golang's structural typing and interface-first approach make it an excellent choice for implementing CQRS. By segregating these responsibilities, we can optimize the read and write models independently.
43+
44+
### 1. The Command Model (Write)
45+
46+
The write model focuses on domain integrity and transactional consistency. In Go, this is typically represented by a set of Command structs and their respective handlers.
47+
48+
```go
49+
// Command definition
50+
type RegisterUser struct {
51+
UserID string
52+
Email string
53+
Password string
54+
}
55+
56+
// Handler implementation
57+
type UserCommandHandler struct {
58+
repository UserRepository
59+
}
60+
61+
func (h *UserCommandHandler) HandleRegister(ctx context.Context, cmd RegisterUser) error {
62+
user, err := domain.NewUser(cmd.UserID, cmd.Email, cmd.Password)
63+
if err != nil {
64+
return err
65+
}
66+
return h.repository.Save(ctx, user)
67+
}
68+
```
69+
70+
### 2. The Query Model (Read)
71+
72+
The read model is optimized for the UI or external API consumers. It often uses DTOs (Data Transfer Objects) and may bypass complex domain logic entirely.
73+
74+
```go
75+
type UserReadModel struct {
76+
ID string `json:"id"`
77+
Email string `json:"email"`
78+
}
79+
80+
type UserQueryHandler struct {
81+
db *sql.DB
82+
}
83+
84+
func (h *UserQueryHandler) GetUserByID(ctx context.Context, id string) (UserReadModel, error) {
85+
// Optimized SQL query directly to a read-optimized view
86+
var model UserReadModel
87+
err := h.db.QueryRowContext(ctx, "SELECT id, email FROM user_views WHERE id = ?", id).Scan(&model.ID, &model.Email)
88+
return model, err
89+
}
90+
```
91+
92+
## Benefits and Considerations
93+
94+
95+
96+
### Independent Scaling and Optimization
97+
98+
CQRS allows you to scale and optimize your read and write operations independently. Since most applications are read-heavy, you can deploy multiple instances of your query services and read-replicas without affecting the write consistency. This is particularly useful when the read model requires complex joins or aggregations that would slow down a transactional write model.
99+
100+
101+
102+
### The "Beware" Clause: Complexity Trade-off
103+
104+
Martin Fowler's primary advice regarding CQRS is that **most systems should stay CRUD**. CQRS introduces a significant "mental leap" and architectural overhead. It should not be the default architecture for an entire system, but rather applied to specific **Bounded Contexts** where the complexity of the domain justifies the cost.
105+
106+
107+
108+
Key risks include:
109+
110+
- **Eventual Consistency:** If using separate databases, the read model will lag behind the write model.
111+
112+
- **Code Duplication:** Managing two models can lead to boilerplate if not handled carefully.
113+
114+
- **Overkill:** Applying CQRS to a simple data-entry application is a classic architectural anti-pattern.
115+
116+
117+
118+
### Relationship with Event Sourcing
119+
120+
While CQRS and **Event Sourcing** are frequently mentioned together, they are distinct patterns. CQRS allows you to use separate models for reads and writes. Event Sourcing ensures that every change to the state is captured as an event.
121+
122+
123+
124+
You can use CQRS without Event Sourcing (using a standard relational database for the write side) and vice versa, though they are highly complementary in high-scale distributed systems.
125+
126+
127+
128+
## Conclusion
129+
130+
131+
132+
CQRS is a powerful tool when applied to the right problems. By acknowledging that reading and writing are fundamentally different behaviors, architects can build more resilient and performant systems. However, as with any advanced pattern, the first rule of CQRS is: **don't use it unless you truly need it.**

public/posts/posts.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
11
[
2+
{
3+
"slug": "understanding-database-normalization-3nf",
4+
"title": "Understanding Database Normalization: The Path to Third Normal Form (3NF)",
5+
"date": "2026-02-06",
6+
"updated": "2026-02-06",
7+
"description": "A professional guide to database normalization, explaining the journey from 1NF to 3NF with clear examples and architectural diagrams.",
8+
"tags": ["database", "sql", "normalization", "3nf", "data-integrity", "dev"],
9+
"category": "dev",
10+
"filename": "understanding-database-normalization-3nf.txt",
11+
"authors": ["fezcode"]
12+
},
13+
{
14+
"slug": "cqrs-in-go-for-geniuses",
15+
"title": "CQRS: Command Query Responsibility Segregation in Modern Architecture",
16+
"date": "2026-02-06",
17+
"updated": "2026-02-06",
18+
"description": "An authoritative guide to CQRS (Command Query Responsibility Segregation) following Martin Fowler's principles, implemented in Golang.",
19+
"tags": ["golang", "architecture", "cqrs", "design-patterns", "backend", "dev"],
20+
"category": "dev",
21+
"filename": "cqrs-in-go-for-geniuses.txt",
22+
"authors": ["fezcode"]
23+
},
224
{
325
"slug": "hyrums-law",
426
"title": "Hyrum's Law: Why Your Bug Fix Broke My Spacebar Heating Workflow",
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Database Normalization: A Clear Guide to 1NF, 2NF, and 3NF
2+
3+
Database normalization often sounds like high-level math, but it's actually just a set of common-sense rules for organizing data. The goal is simple: **Don't repeat yourself.**
4+
5+
When data is repeated, you run into "Anomalies"—bugs where you update a piece of info in one row but forget it in another. Here is how we get to the industry standard: **Third Normal Form (3NF).**
6+
7+
---
8+
9+
## 1. First Normal Form (1NF): No Lists in Cells
10+
11+
The first rule is that every cell must contain exactly **one** value. You cannot have a list of items inside a single column.
12+
13+
### The "Messy" Table (Not in 1NF)
14+
Notice how "Courses" has multiple values. This makes it impossible to search for everyone taking "Math."
15+
16+
| StudentID | Name | Courses |
17+
| :--- | :--- | :--- |
18+
| 101 | Alice | Math, Physics |
19+
| 102 | Bob | Biology |
20+
21+
### The 1NF Solution
22+
We split the rows so every cell is "Atomic" (indivisible).
23+
24+
| StudentID | Name | Course |
25+
| :--- | :--- | :--- |
26+
| 101 | Alice | Math |
27+
| 101 | Alice | Physics |
28+
| 102 | Bob | Biology |
29+
30+
---
31+
32+
## 2. Second Normal Form (2NF): The "Whole Key" Rule
33+
34+
2NF only matters when you have a **Composite Key** (a primary key made of two or more columns). It says: "Every column must depend on the *entire* key, not just part of it."
35+
36+
### The Problem (In 1NF, but not 2NF)
37+
In this table, the Primary Key is `(StudentID + CourseID)`.
38+
39+
| StudentID (PK) | CourseID (PK) | Grade | Teacher_Office |
40+
| :--- | :--- | :--- | :--- |
41+
| 101 | CS50 | A | Room 402 |
42+
| 102 | CS50 | B | Room 402 |
43+
44+
**The Issue:** `Grade` depends on both the student and the course. But `Teacher_Office` depends *only* on the `CourseID`. Alice's grade doesn't change the teacher's office. This is a "Partial Dependency."
45+
46+
```mermaid
47+
graph TD
48+
subgraph PrimaryKey
49+
A[StudentID]
50+
B[CourseID]
51+
end
52+
A & B --> Grade
53+
B -->|Partial Dependency| Office[Teacher_Office]
54+
```
55+
56+
### The 2NF Solution
57+
Move the partial dependency into its own table. Now, if the teacher moves offices, you only change it in one row.
58+
59+
**Table: Enrollments**
60+
| StudentID | CourseID | Grade |
61+
| :--- | :--- | :--- |
62+
63+
**Table: Courses**
64+
| CourseID | Teacher_Office |
65+
| :--- | :--- |
66+
67+
---
68+
69+
## 3. Third Normal Form (3NF): No "Friends of Friends"
70+
71+
3NF says: "A column cannot depend on another column that isn't the primary key." This is called a **Transitive Dependency**.
72+
73+
### The Problem (In 2NF, but not 3NF)
74+
Here, the Primary Key is `EmployeeID`.
75+
76+
| EmployeeID (PK) | Name | DeptID | DeptName |
77+
| :--- | :--- | :--- | :--- |
78+
| E01 | Alice | D01 | Engineering |
79+
| E02 | Bob | D01 | Engineering |
80+
81+
**The Issue:** `Name` depends on `EmployeeID` (Good). `DeptID` depends on `EmployeeID` (Good). But `DeptName` depends on `DeptID`. It only knows the EmployeeID *through* the Department.
82+
83+
```mermaid
84+
graph LR
85+
ID[EmployeeID] --> DeptID
86+
DeptID --> DeptName
87+
ID -.->|Indirect / Transitive| DeptName
88+
```
89+
90+
If you hire a new department head but have no employees in that department yet, you can't even put the department name in the database!
91+
92+
### The 3NF Solution
93+
Split them so non-keys only talk to the Primary Key.
94+
95+
**Table: Employees**
96+
| EmployeeID | Name | DeptID |
97+
| :--- | :--- | :--- |
98+
99+
**Table: Departments**
100+
| DeptID | DeptName |
101+
| :--- | :--- |
102+
103+
---
104+
105+
## The Golden Rule
106+
107+
To remember all of this, software engineers use a famous quote by Bill Kent. He said that in a normalized database, every column must depend on:
108+
109+
> **"The Key, the Whole Key, and Nothing but the Key."**
110+
111+
1. **The Key:** (1NF) Everything belongs to a key.
112+
2. **The Whole Key:** (2NF) Don't depend on just *part* of a composite key.
113+
3. **Nothing but the Key:** (3NF) Don't depend on other non-key columns.
114+
115+
By following these steps, you ensure your data is lean, accurate, and incredibly hard to break during updates.

0 commit comments

Comments
 (0)