MVC (Model-View-Controller) separates data, presentation, and logic. Today you will implement MVC in a vanilla TypeScript app and see how Express and React map to the same concerns.
// Model — pure data + business rules
class TodoModel {
private todos: Todo[] = [];
getAll() { return [...this.todos]; }
add(text: string): Todo {
const todo = { id: Date.now(), text, done: false };
this.todos.push(todo);
return todo;
}
toggle(id: number) {
const todo = this.todos.find(t => t.id === id);
if (todo) todo.done = !todo.done;
}
delete(id: number) {
this.todos = this.todos.filter(t => t.id !== id);
}
}
// View — renders HTML, emits user events
class TodoView {
private addBtn = document.getElementById('add-btn')!;
private input = document.getElementById('new-todo') as HTMLInputElement;
private list = document.getElementById('todo-list')!;
onAdd?: (text: string) => void;
constructor() {
this.addBtn.addEventListener('click', () => {
this.onAdd?.(this.input.value);
this.input.value = '';
});
}
render(todos: Todo[]) {
this.list.innerHTML = todos.map(t => \`
<li data-id="\${t.id}" class="\${t.done ? 'done' : ''}">
\${t.text}
</li>\`).join('');
}
}
// Controller — connects Model and View
class TodoController {
constructor(private model: TodoModel, private view: TodoView) {
this.view.onAdd = (text) => {
this.model.add(text);
this.view.render(this.model.getAll());
};
this.view.render(this.model.getAll());
}
}
new TodoController(new TodoModel(), new TodoView());