2025年1月1日
アーキテクチャガイド
Roxは保守性、テスト可能性、柔軟性を確保するために実証済みの設計パターンで構築されています。
コア設計パターン
リポジトリパターン
リポジトリパターンはインターフェースを通じてデータベース操作を抽象化し、ビジネスロジックをデータアクセス実装から分離します。
メリット
- テスト可能性: ユニットテストのためにリポジトリを簡単にモック化
- 柔軟性: ビジネスロジックを変更せずにデータベース実装を切り替え
- 保守性: 関心事の明確な分離
主要なリポジトリ
interface INoteRepository {
create(note: Note): Promise<Note>;
findById(id: string): Promise<Note | null>;
findByUserId(userId: string): Promise<Note[]>;
update(id: string, note: Partial<Note>): Promise<Note>;
delete(id: string): Promise<void>;
}
interface IUserRepository {
create(user: User): Promise<User>;
findById(id: string): Promise<User | null>;
findByUsername(username: string): Promise<User | null>;
update(id: string, user: Partial<User>): Promise<User>;
delete(id: string): Promise<void>;
}
アダプターパターン
アダプターパターンは異なるストレージバックエンドに対して一貫したインターフェースを提供します。
interface IStorageAdapter {
upload(file: Buffer, key: string): Promise<string>;
download(key: string): Promise<Buffer>;
delete(key: string): Promise<void>;
getUrl(key: string): string;
}
依存性注入
RoxはHonoのコンテキストを使用して、環境変数に基づいて依存関係を注入します。
app.use('*', async (c, next) => {
const db = createDatabase(c.env.DB_TYPE);
c.set('db', db);
const noteRepository = new DrizzleNoteRepository(db);
c.set('noteRepository', noteRepository);
await next();
});
プロジェクト構造
packages/
├── backend/
│ ├── src/
│ │ ├── adapters/ # ストレージアダプター
│ │ ├── repositories/ # データベースリポジトリ
│ │ ├── services/ # ビジネスロジック
│ │ ├── routes/ # APIルート
│ │ └── middleware/ # ミドルウェア
│ └── tests/ # テスト
├── frontend/
│ ├── src/
│ │ ├── components/ # Reactコンポーネント
│ │ ├── pages/ # Wakuページ
│ │ └── atoms/ # Jotai atoms
│ └── public/ # 静的アセット
└── shared/
└── types/ # 共有TypeScript型
データベース層
Drizzle ORM
Roxは型安全なデータベース操作のためにDrizzle ORMを使用しています。
export const users = pgTable('users', {
id: text('id').primaryKey(),
username: text('username').notNull().unique(),
displayName: text('display_name'),
createdAt: timestamp('created_at').defaultNow(),
});
マイグレーション
# マイグレーション生成
bun run db:generate
# マイグレーション実行
bun run db:migrate
# Drizzle Studioを開く
bun run db:studio
API層
Honoフレームワーク
Roxは軽量で高速なAPIサーバーのためにHonoを使用しています。
app.get('/api/notes/:id', async (c) => {
const noteRepository = c.get('noteRepository');
const note = await noteRepository.findById(c.req.param('id'));
if (!note) {
return c.json({ error: 'Not found' }, 404);
}
return c.json(note);
});
フロントエンドアーキテクチャ
Waku (React Server Components)
RoxフロントエンドはサーバーサイドレンダリングとReact Server ComponentsのためにWakuを使用しています。
export default async function NotePage({ id }: { id: string }) {
const note = await fetchNote(id);
return (
<div>
<NoteContent note={note} />
<NoteActions noteId={id} />
</div>
);
}
状態管理 (Jotai)
export const currentUserAtom = atom<User | null>(null);
// コンポーネントでの使用
const [user, setUser] = useAtom(currentUserAtom);
パフォーマンス考慮事項
キャッシング
RoxはRedis(Dragonfly経由)をキャッシングに使用:
const cacheService = new CacheService(redis);
await cacheService.set(`user:${userId}`, user, 3600);
画像最適化
画像は自動的にWebPに変換されます:
const imageProcessor = new ImageProcessor();
const optimized = await imageProcessor.toWebP(buffer);
セキュリティ
入力検証
すべての入力はZodを使用して検証されます:
const createNoteSchema = z.object({
text: z.string().min(1).max(3000),
visibility: z.enum(['public', 'unlisted', 'followers', 'direct']),
});
レート制限
app.use('/api/*', rateLimiter({
windowMs: 15 * 60 * 1000,
max: 100,
}));
次のステップ
- デプロイメントガイド - Roxを本番環境にデプロイ
- APIリファレンス - APIを探索
- コントリビューション - Roxに貢献