Sử dụng với Electron
Các ứng dụng Electron có kiến trúc đặc biệt gồm nhiều process với các trách nhiệm khác nhau. Việc áp dụng FSD trong bối cảnh như vậy yêu cầu phải thích nghi cấu trúc với các đặc điểm của Electron.
Thư mụcsrc
Thư mụcapp Common app layer
Thư mụcmain Main process
- index.ts Main process entry point
Thư mụcpreload Preload script and Context Bridge
- index.ts Preload entry point
Thư mụcrenderer Renderer process
- index.html Renderer process entry point
Thư mụcmain
Thư mụcfeatures
Thư mụcuser
Thư mụcipc
- get-user.ts
- send-user.ts
- entities
- shared
Thư mụcrenderer
Thư mụcpages
Thư mụcsettings
Thư mụcipc
- get-user.ts
- save-user.ts
Thư mụcui
- user.tsx
- index.ts
Thư mụchome
Thư mụcui
- home.tsx
- index.ts
- widgets
- features
- entities
- shared
Thư mụcshared Common code between main and renderer
- ipc IPC description (event names, contracts)
Quy tắc Public API
Phần tiêu đề “Quy tắc Public API”Mỗi process phải có public API riêng của nó. Ví dụ, bạn không thể import các module từ main vào renderer.
Chỉ có thư mục src/shared là public cho cả hai process.
Nó cũng cần thiết để mô tả các hợp đồng cho tương tác giữa các process.
Các thay đổi bổ sung cho cấu trúc chuẩn
Phần tiêu đề “Các thay đổi bổ sung cho cấu trúc chuẩn”Được đề xuất sử dụng segment ipc mới, nơi diễn ra tương tác giữa các process.
Các layer pages và widgets, dựa trên tên gọi của chúng, không nên có mặt trong src/main. Bạn có thể sử dụng features, entities và shared.
Layer app trong src chứa các điểm đầu vào cho main và renderer, cũng như IPC.
Không mong muốn các segment trong layer app có điểm giao nhau
Ví dụ về tương tác
Phần tiêu đề “Ví dụ về tương tác”export const CHANNELS = { GET_USER_DATA: 'GET_USER_DATA', SAVE_USER: 'SAVE_USER',} as const;
export type TChannelKeys = keyof typeof CHANNELS;import { CHANNELS } from './channels';
export interface IEvents { [CHANNELS.GET_USER_DATA]: { args: void, response?: { name: string; email: string; }; }; [CHANNELS.SAVE_USER]: { args: { name: string; }; response: void; };}import { CHANNELS } from './channels';import type { IEvents } from './events';
type TOptionalArgs<T> = T extends void ? [] : [args: T];
export type TElectronAPI = { [K in keyof typeof CHANNELS]: (...args: TOptionalArgs<IEvents[typeof CHANNELS[K]]['args']>) => IEvents[typeof CHANNELS[K]]['response'];};import { contextBridge, ipcRenderer } from 'electron';import { CHANNELS, type TElectronAPI } from 'shared/ipc';
const API: TElectronAPI = { [CHANNELS.GET_USER_DATA]: () => ipcRenderer.sendSync(CHANNELS.GET_USER_DATA), [CHANNELS.SAVE_USER]: args => ipcRenderer.invoke(CHANNELS.SAVE_USER, args),} as const;
contextBridge.exposeInMainWorld('electron', API);import { ipcMain } from 'electron';import { CHANNELS } from 'shared/ipc';
export const sendUser = () => { ipcMain.on(CHANNELS.GET_USER_DATA, ev => { ev.returnValue = { name: 'John Doe', email: 'john.doe@example.com', }; });};import { CHANNELS } from 'shared/ipc';
export const getUser = () => { const user = window.electron[CHANNELS.GET_USER_DATA]();
return user ?? { name: 'John Donte', email: 'john.donte@example.com' };};