What is this?
This post is a set of notes from an attempt to go back to basics and think through how to choose an architecture.
There are plenty of modern options out there—Feature-Sliced Design (FSD), Vertical Slice Architecture, and so on—but I wanted to revisit what criteria we should actually use when picking one.
A Map of Architecture
To make sense of the landscape, I drew up the following map for studying it.
It moves from design principles, to architectures, down to individual design patterns, getting more specific as it goes.
ソフトウェア設計
│
├── 設計原則
│ ├── Separation of Concerns
│ ├── 高凝集・低結合
│ ├── コロケーション
│ ├── Dependency Inversion
│ ├── SRP
│ └── DRY / KISS / YAGNI
│
├── アプリケーション全体の構造
│ ├── Layered Architecture
│ ├── Feature-Based Architecture
│ ├── Vertical Slice Architecture
│ ├── Clean Architecture
│ └── Feature-Sliced Design
│
├── UI・Presentationの設計
│ ├── MVC
│ ├── MVP
│ └── MVVM
│
└── より細かな設計パターン
├── Repository
├── Factory
├── Adapter
└── Dependency Injection
First things first...
The very first question to ask is: what is this code responsible for?
Say you're building a product list screen. You'd write out tasks like these:
商品を取得する
商品を表示する
商品を検索する
商品を並び替える
商品を編集する
(Fetch products, display products, search products, sort products, edit products.)
You could build all of this as one giant component, but I think we all know how painful that becomes to maintain.
Reference: https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html?utm_source=chatgpt.com
This is where the design question begins: how much of this should live together in the same module?
Cohesion and Coupling
Cohesion and coupling are the two key concepts here. https://qiita.com/dsudo/items/ee3fee1f558c7f1b359f
Cohesion asks: is related code kept together?
Coupling asks: how much does this code depend on other code?
That's the gist of it.
For example, consider a codebase organized like this:
components/
ProductList.tsx
hooks/
useProducts.ts
api/
products.ts
types/
product.ts
tests/
ProductList.test.tsx
It's neatly organized by technical category, but to work on the product list you have to hop between all these different directories.
What if we wrote it like this instead?
features/
└── products/
├── ProductList.tsx
├── useProducts.ts
├── api.ts
├── types.ts
└── ProductList.test.tsx
With this layout, all the code related to products lives in one place.
This approach is what leads to Feature-based Architecture and Vertical Slice Architecture.
On Colocation
colocation
Place code as close to where it’s relevant as possible
It's not about grouping code of the same kind—
it's about keeping code that changes together
close together.
https://kentcdodds.com/blog/colocation?utm_source=chatgpt.com
Layered Architecture
Now we get into architectures proper.
Layered Architecture divides an application by technical responsibility.
Presentation
↓
Application
↓
Domain
↓
Infrastructure
The benefit is that responsibilities are easy to see.
However, unlike the approaches we've discussed so far, changing a feature may mean making changes that cut across multiple layers.
In other words, while the code is separated technically, each feature ends up scattered across the codebase.
Vertical Slice Architecture
Vertical Slice Architecture takes the opposite approach to Layered Architecture.
The idea is to group related logic along the axis of change.
features/
├── create-order/
├── cancel-order/
├── get-order-list/
└── update-order/
You can structure it like that, or like this:
create-order/
├── UI
├── validation
├── application logic
├── database access
└── tests
Both work.
In short, you slice the application vertically by feature (use case).
https://www.jimmybogard.com/vertical-slice-architecture/?utm_source=chatgpt.com
Feature-Sliced Design
Now for FSD, which seems to be gaining real traction in the React community. When I first started writing React professionally, Atomic Design was just taking off—and lately the sentiment seems to be shifting toward FSD as the better option.
FSD has official documentation, and it's worth reading through.
I don't claim to fully understand it yet, but here are my notes...
app
pages
widgets
features
entities
shared
You set up layers like these.
Layer
↓
Slice
↓
Segment
And organize things in these three tiers.
features/
└── add-to-cart/
├── ui/
├── api/
└── model/
In this example, the mapping is:
features: Layer
add-to-card: Slice
ui, api, model: Segment
That's how the pieces correspond.
On Responsibilities
When it comes to dividing up responsibilities around the UI, MVC, MVP, and MVVM come up.
First, what does "responsibility" mean in software?
What a piece of code is in charge of, and what decisions it is responsible for making
That's roughly what it means.
For a product list, for example, you have tasks like:
1. 商品データを取得する
2. 商品データを保持する
3. 検索条件を管理する
4. 商品を絞り込む
5. HTMLとして表示する
6. ボタンが押されたときの処理をする
(Fetch product data, hold product data, manage search conditions, filter products, render as HTML, handle button clicks.)
You could write all of this in a single component, but then that component gets modified for all sorts of reasons—API spec changes, design changes, changes to the search logic, and so on.
That is what it looks like when a component has too many responsibilities.
With MVC, you split things into Model, View, and Controller:
Model
商品
価格
在庫
検索処理
View
商品名を表示
価格を表示
ボタンを表示
Controller
検索ボタンが押された
↓
Modelに検索させる
↓
結果をViewへ反映
That's how the responsibilities are divided.
MVVM separates responsibilities in a similar way.
View
↓
「productsを表示するだけ」
ViewModel
↓
「products、isLoading、errorをView用に準備する」
Model
↓
「商品データやビジネスルール」
Further Reading
Official FSD documentation
The docs explain how FSD differs from other architectures, which I found helpful.
A Reddit thread on Vertical Slice Architecture
I found this one useful for how it makes the case for the approach with a focus on colocation.
