Back to list
naporin0624

cqrs-pattern

by naporin0624

0🍴 0📅 Jan 9, 2026

SKILL.md


name: cqrs-pattern description: Implement CQRS (Command Query Responsibility Segregation) pattern for Electron services. Use when creating service methods, separating read/write operations, or implementing reactive data streams with Observable for queries and ResultAsync for commands. allowed-tools: Read, Write, Edit, Grep, Glob

CQRS Pattern for Electron Services

Overview

This skill documents the CQRS (Command Query Responsibility Segregation) pattern used in Electron applications with RxJS and neverthrow.

Core Principles

AspectQuery (Read)Command (Write)
PurposeRead data without side effectsModify state
Return TypeObservable<T>ResultAsync<void, Error>
Side EffectsNoneDatabase writes, notifications
IdempotencyAlways idempotentMay not be idempotent

Quick Start

1. Define Service Interface

// src/shared/services/user.service.d.ts
import type { Observable } from 'rxjs';
import type { ResultAsync } from 'neverthrow';
import type { ApplicationError } from '@shared/typing';

interface UserService {
  // Queries - Return Observable
  list(): Observable<User[]>;
  get(id: string): Observable<User | undefined>;

  // Commands - Return ResultAsync
  create(data: CreateUserData): ResultAsync<void, ApplicationError>;
  update(data: UpdateUserData): ResultAsync<void, ApplicationError>;
  delete(id: string): ResultAsync<void, ApplicationError>;
}

2. Implement Service with BehaviorSubject Bridge

// src/main/services/user.service.ts
import { BehaviorSubject, Observable, concat, from } from 'rxjs';
import { distinctUntilChanged, mergeMap } from 'rxjs/operators';
import { ResultAsync, okAsync, errAsync } from 'neverthrow';

export class UserServiceImpl implements UserService {
  #notify = new BehaviorSubject(Date.now());

  constructor(private readonly db: Database) {}

  // QUERY: Returns Observable
  list(): Observable<User[]> {
    return concat(
      from(this.#getUsers()),                    // Initial fetch
      this.#notify.pipe(
        distinctUntilChanged(),
        mergeMap(() => this.#getUsers())         // Re-fetch on changes
      )
    );
  }

  // COMMAND: Returns ResultAsync
  create(data: CreateUserData): ResultAsync<void, ApplicationError> {
    return this.#insertUser(data)
      .andThen(() => {
        this.#notify.next(Date.now());           // Notify subscribers
        return okAsync(void 0);
      });
  }

  // Private: Actual database operation
  async #getUsers(): Promise<User[]> {
    return this.db.query.users.findMany();
  }

  #insertUser(data: CreateUserData): ResultAsync<void, ApplicationError> {
    return ResultAsync.fromPromise(
      this.db.insert(users).values(data),
      (e) => new ApplicationError('Failed to create user', e)
    ).map(() => void 0);
  }
}

Architecture Diagram

┌─────────────────────────────────────────────────────────────┐
│                   SERVICE LAYER (CQRS)                       │
├─────────────────────────────────────────────────────────────┤
│                                                               │
│   #notify = new BehaviorSubject(Date.now())                  │
│              │                                                │
│   ┌──────────┴──────────┐                                    │
│   │                     │                                    │
│   ▼                     ▼                                    │
│ QUERY                 COMMAND                                │
│ Observable<T>         ResultAsync<void>                      │
│   │                     │                                    │
│   │ concat(             │ #performOperation()               │
│   │   from(#getData()), │   .andThen(() => {                │
│   │   #notify.pipe(     │     #notify.next(Date.now());     │
│   │     mergeMap(...)   │     return okAsync(void 0);       │
│   │   )                 │   })                               │
│   │ )                   │                                    │
│   │                     │                                    │
│   └──────────┬──────────┘                                    │
│              │                                                │
│              ▼                                                │
│        DATABASE                                              │
└─────────────────────────────────────────────────────────────┘

When to Use

Use QUERY (Observable) for:

  • Listing entities: list(), histories()
  • Getting single entity: get(id), active()
  • Streaming updates: notify() (returns Subject/Observable)
  • Any read operation that benefits from reactive updates

Use COMMAND (ResultAsync) for:

  • Creating entities: create(data)
  • Updating entities: update(data)
  • Deleting entities: delete(id)
  • Actions with side effects: invite(), send()

Additional Resources

Score

Total Score

60/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon