Building a casino platform sounds fun — and it is — but it's also a surprisingly rigorous software engineering challenge. You're managing real-time game state, financial transactions (chips), user sessions, and a complex rule engine for each game type, all within a single application. Getting the architecture wrong early means exponential pain later.
Why MVC + Service Layer?
Pure MVC works for simple CRUD apps, but casino games are business-logic-heavy. A controller that directly handles blackjack hand evaluation, bet validation, chip ledger updates, and database writes would be impossible to test and maintain. The Service Layer pattern solves this by placing all business logic in dedicated service classes that controllers merely orchestrate.
- ›Controllers: handle HTTP request/response, validate input shape, call service methods
- ›Services: encapsulate game logic, chip transactions, win/loss calculations — no HTTP concerns
- ›Models: ORM-style data access only — no business logic lives here
- ›Routes: thin wiring layer, middleware application
This separation is what made 67 unit tests viable. Each service method is a pure function of its inputs — no HTTP context, no database calls (mocked) — which means tests are fast, isolated, and reliable. A blackjack `evaluateHand()` function just takes cards and returns a result; it doesn't care about Express or MySQL.
Testing Strategy
The test suite focuses on the game engine — the highest-risk code where a bug means players win or lose unfairly. Edge cases like blackjack natural wins, split aces, dealer soft-17 rules, and bust conditions are each covered by dedicated test cases.
1// Example: testing blackjack bust detection
2describe('BlackjackService.evaluateHand', () => {
3 it('should detect player bust when hand value exceeds 21', () => {
4 const hand = [
5 { suit: 'hearts', value: 'K' }, // 10
6 { suit: 'clubs', value: '9' }, // 9
7 { suit: 'spades', value: '5' }, // 5 → total: 24
8 ];
9 const result = BlackjackService.evaluateHand(hand);
10 expect(result.total).toBe(24);
11 expect(result.bust).toBe(true);
12 });
13});The 6-Theme Frontend System
Rather than building six separate UIs, I implemented a CSS custom property system where a single theme class on the `<body>` element cascades through the entire UI. Each theme defines overrides for background, card felt color, chip palette, and accent highlights. JavaScript theme-switching updates the class and persists the preference in localStorage.
Chart.js powers the analytics dashboard, showing chip balance history, win/loss trends, and game-specific stats. The charts dynamically re-color their datasets when the theme changes by reading the current CSS custom property values.
💡 Takeaway: When building a complex application, invest in architecture before features. The Service Layer decision made testing straightforward and new game implementations (roulette, craps) easy to add without touching existing game code.