Usage with Electron
このコンテンツはまだ日本語訳がありません。
Electron applications have a special architecture consisting of multiple processes with different responsibilities. Applying FSD in such a context requires adapting the structure to the Electron specifics.
ディレクトリsrc
ディレクトリapp Common app layer
ディレクトリmain Main process
- index.ts Main process entry point
ディレクトリpreload Preload script and Context Bridge
- index.ts Preload entry point
ディレクトリrenderer Renderer process
- index.html Renderer process entry point
ディレクトリmain
ディレクトリfeatures
ディレクトリuser
ディレクトリipc
- get-user.ts
- send-user.ts
- entities
- shared
ディレクトリrenderer
ディレクトリpages
ディレクトリsettings
ディレクトリipc
- get-user.ts
- save-user.ts
ディレクトリui
- user.tsx
- index.ts
ディレクトリhome
ディレクトリui
- home.tsx
- index.ts
- widgets
- features
- entities
- shared
ディレクトリshared Common code between main and renderer
- ipc IPC description (event names, contracts)
Public API rules
Section titled “Public API rules”Each process must have its own public API. For example, you can’t import modules from main to renderer.
Only the src/shared folder is public for both processes.
It’s also necessary for describing contracts for process interaction.
Additional changes to the standard structure
Section titled “Additional changes to the standard structure”It’s suggested to use a new ipc segment, where interaction between processes takes place.
The pages and widgets layers, based on their names, should not be present in src/main. You can use features, entities and shared.
The app layer in src contains entry points for main and renderer, as well as the IPC.
It’s not desirable for segments in the app layer to have intersection points
Interaction example
Section titled “Interaction example”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' };};