@qlover/create-app 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/react-app/README.en.md +257 -0
  5. package/dist/templates/react-app/README.md +29 -231
  6. package/dist/templates/react-app/__tests__/__mocks__/I18nService.ts +13 -0
  7. package/dist/templates/react-app/__tests__/__mocks__/MockAppConfit.ts +48 -0
  8. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +16 -0
  9. package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +14 -0
  10. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +92 -0
  11. package/dist/templates/react-app/__tests__/setup/index.ts +51 -0
  12. package/dist/templates/react-app/__tests__/src/App.test.tsx +139 -0
  13. package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +288 -0
  14. package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +102 -0
  15. package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +228 -0
  16. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +207 -0
  17. package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +181 -0
  18. package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +61 -0
  19. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +199 -0
  20. package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +192 -0
  21. package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +235 -0
  22. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +224 -0
  23. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +257 -0
  24. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +72 -0
  25. package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +62 -0
  26. package/dist/templates/react-app/__tests__/src/main.test.tsx +46 -0
  27. package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +88 -0
  28. package/dist/templates/react-app/config/app.router.ts +155 -0
  29. package/dist/templates/react-app/config/common.ts +9 -1
  30. package/dist/templates/react-app/docs/en/bootstrap.md +562 -0
  31. package/dist/templates/react-app/docs/en/development-guide.md +523 -0
  32. package/dist/templates/react-app/docs/en/env.md +482 -0
  33. package/dist/templates/react-app/docs/en/global.md +509 -0
  34. package/dist/templates/react-app/docs/en/i18n.md +268 -0
  35. package/dist/templates/react-app/docs/en/index.md +173 -0
  36. package/dist/templates/react-app/docs/en/ioc.md +424 -0
  37. package/dist/templates/react-app/docs/en/project-structure.md +434 -0
  38. package/dist/templates/react-app/docs/en/request.md +425 -0
  39. package/dist/templates/react-app/docs/en/router.md +404 -0
  40. package/dist/templates/react-app/docs/en/store.md +321 -0
  41. package/dist/templates/react-app/docs/en/test-guide.md +782 -0
  42. package/dist/templates/react-app/docs/en/theme.md +424 -0
  43. package/dist/templates/react-app/docs/en/typescript-guide.md +473 -0
  44. package/dist/templates/react-app/docs/zh/bootstrap.md +7 -0
  45. package/dist/templates/react-app/docs/zh/development-guide.md +523 -0
  46. package/dist/templates/react-app/docs/zh/env.md +24 -25
  47. package/dist/templates/react-app/docs/zh/global.md +28 -27
  48. package/dist/templates/react-app/docs/zh/i18n.md +268 -0
  49. package/dist/templates/react-app/docs/zh/index.md +173 -0
  50. package/dist/templates/react-app/docs/zh/ioc.md +44 -32
  51. package/dist/templates/react-app/docs/zh/project-structure.md +434 -0
  52. package/dist/templates/react-app/docs/zh/request.md +429 -0
  53. package/dist/templates/react-app/docs/zh/router.md +408 -0
  54. package/dist/templates/react-app/docs/zh/store.md +321 -0
  55. package/dist/templates/react-app/docs/zh/test-guide.md +782 -0
  56. package/dist/templates/react-app/docs/zh/theme.md +424 -0
  57. package/dist/templates/react-app/docs/zh/typescript-guide.md +473 -0
  58. package/dist/templates/react-app/package.json +9 -20
  59. package/dist/templates/react-app/src/base/cases/AppConfig.ts +16 -9
  60. package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +7 -1
  61. package/dist/templates/react-app/src/base/services/I18nService.ts +15 -4
  62. package/dist/templates/react-app/src/base/services/RouteService.ts +43 -7
  63. package/dist/templates/react-app/src/core/bootstraps/BootstrapApp.ts +31 -10
  64. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +1 -1
  65. package/dist/templates/react-app/src/core/globals.ts +1 -3
  66. package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +5 -3
  67. package/dist/templates/react-app/src/main.tsx +6 -1
  68. package/dist/templates/react-app/src/pages/404.tsx +0 -1
  69. package/dist/templates/react-app/src/pages/500.tsx +1 -1
  70. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +3 -1
  71. package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +3 -1
  72. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +1 -1
  73. package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +6 -1
  74. package/dist/templates/react-app/src/styles/css/page.css +1 -1
  75. package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +9 -2
  76. package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +5 -3
  77. package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +4 -6
  78. package/dist/templates/react-app/tsconfig.json +2 -1
  79. package/dist/templates/react-app/tsconfig.test.json +13 -0
  80. package/dist/templates/react-app/vite.config.ts +3 -2
  81. package/package.json +1 -1
@@ -0,0 +1,562 @@
1
+ # Project Bootstrap Guide
2
+
3
+ ## What is Bootstrap?
4
+
5
+ Bootstrap is an application starter that helps us manage various initialization logic uniformly when the application starts.
6
+
7
+ **In simple terms**: Just like a computer needs to start various services during boot-up, our application also needs to do some preparation work when starting, such as:
8
+
9
+ - Check if user is logged in
10
+ - Load user information
11
+ - Initialize API configuration
12
+ - Set theme, language, etc.
13
+
14
+ File path: src/core/bootstraps
15
+
16
+ ## Implementation in Project
17
+
18
+ This project is based on the AsyncExecutor of `@qlover/fe-corekit`, and `corekit-bridge` implements Bootstrap on this foundation.
19
+
20
+ **File Entry**: `src/core/bootstraps/BootstrapApp.ts`
21
+
22
+ **Main Components**:
23
+
24
+ 1. [IOC Container](./ioc.md) - Dependency injection management
25
+ 2. [Environment Variable Injection](./env.md) - Configuration management
26
+ 3. [Browser Global Variable Injection](./global.md) - Browser global properties
27
+
28
+ ## When to Use Bootstrap?
29
+
30
+ Bootstrap comes in handy when you encounter the following situations:
31
+
32
+ ### Why Need a "Bootstrapper"
33
+
34
+ **Core Goal**: Make application startup cleaner and business logic clearer.
35
+
36
+ **Problems Solved**: When a page opens, you need authentication or need to request an API before entering the page, you might do something like this:
37
+
38
+ ```tsx
39
+ export function App() {
40
+ const [loading, setLoading] = useState(false);
41
+
42
+ useEffect(() => {
43
+ setLoading(true);
44
+
45
+ fetchUserInfo()
46
+ .then(() => {
47
+ setLoading(false);
48
+ })
49
+ .catch(() => {
50
+ setLoading(false);
51
+ });
52
+ }, []);
53
+
54
+ if (loading) {
55
+ return <div>loading ...</div>;
56
+ }
57
+
58
+ return <Router></Router>;
59
+ }
60
+ ```
61
+
62
+ This code works fine, but the key issue is that it's tightly coupled with the component! That means when fetchUserInfo succeeds, it updates local state and then renders Router.
63
+
64
+ However, if the logic for entering Router becomes complex and depends on various conditions, the component becomes very bloated and hard to maintain.
65
+
66
+ For example, consider this situation:
67
+
68
+ When the route is /home, request user information, after success, if the user's roles have permission then enter, otherwise redirect to /login
69
+
70
+ ```tsx
71
+ export function App() {
72
+ const [loading, setLoading] = useState(false);
73
+ const [userInfo, setUserInfo] = useState(null);
74
+ const [hasPermission, setHasPermission] = useState(false);
75
+ const location = useLocation();
76
+
77
+ useEffect(() => {
78
+ setLoading(true);
79
+
80
+ // Check current route
81
+ if (location.pathname === '/home') {
82
+ fetchUserInfo()
83
+ .then((user) => {
84
+ setUserInfo(user);
85
+
86
+ // Check user permissions
87
+ if (user.roles && user.roles.includes('admin')) {
88
+ setHasPermission(true);
89
+ setLoading(false);
90
+ } else {
91
+ // No permission, redirect to login page
92
+ window.location.href = '/login';
93
+ }
94
+ })
95
+ .catch((error) => {
96
+ console.error('Failed to fetch user info:', error);
97
+ // Also redirect to login page on request failure
98
+ window.location.href = '/login';
99
+ });
100
+ } else {
101
+ setLoading(false);
102
+ }
103
+ }, [location.pathname]);
104
+
105
+ // If loading, show loading state
106
+ if (loading) {
107
+ return <div>Loading...</div>;
108
+ }
109
+
110
+ // If home page but no permission, show error message
111
+ if (location.pathname === '/home' && !hasPermission) {
112
+ return <div>Access denied</div>;
113
+ }
114
+
115
+ return <Router />;
116
+ }
117
+ ```
118
+
119
+ This example shows several issues:
120
+
121
+ 1. **Component Has Too Many Responsibilities**: App component needs to handle routing, user authentication, permission checking, error handling, etc.
122
+ 2. **Complex State Management**: Need to manage multiple states like loading, userInfo, hasPermission
123
+ 3. **Logic Coupling**: Authentication logic mixed with component rendering logic
124
+ 4. **Hard to Test**: Component contains too much business logic, making unit testing difficult
125
+ 5. **Hard to Extend**: If more permission checks or authentication logic need to be added later, the component becomes more bloated
126
+
127
+ These problems will amplify step by step after project iterations until you start refactoring code!
128
+
129
+ ## What is a Bootstrapper?
130
+
131
+ A bootstrapper is a front-end logic processor that runs at the UI layer, with core functions:
132
+
133
+ 1. **Front-end Logic Processing**: Execute necessary initialization logic before application rendering
134
+ 2. **State Management**: Manage application state through store, achieving reactive UI updates
135
+ 3. **Separation of Concerns**: Separate business logic from UI components
136
+
137
+ ### Core Features
138
+
139
+ - **Asynchronous Execution**: Implement asynchronous logic processing based on AsyncExecutor
140
+ - **State Driven**: Trigger UI updates through store state changes
141
+ - **Modular**: Support IOC container, environment variable injection, global variable injection, and other modules
142
+
143
+ ### Workflow
144
+
145
+ ```
146
+ App Start → Bootstrap Initialization → Execute Front-end Logic → Update Store → UI Responds to Updates
147
+ ```
148
+
149
+ ### Comparison with Traditional Approach
150
+
151
+ **Traditional Approach**:
152
+
153
+ ```tsx
154
+ // Business logic mixed in component
155
+ function App() {
156
+ const [loading, setLoading] = useState(true);
157
+ const [data, setData] = useState(null);
158
+
159
+ useEffect(() => {
160
+ // Business logic mixed in component
161
+ fetchData()
162
+ .then(setData)
163
+ .finally(() => setLoading(false));
164
+ }, []);
165
+
166
+ if (loading) return <Loading />;
167
+ return <MainContent data={data} />;
168
+ }
169
+ ```
170
+
171
+ **Using Bootstrapper**:
172
+
173
+ ```tsx
174
+ // Component only focuses on rendering
175
+ function App() {
176
+ const { loading, data } = useStore(); // Get state from store
177
+
178
+ if (loading) return <Loading />;
179
+ return <MainContent data={data} />;
180
+ }
181
+
182
+ // Business logic handled in bootstrapper
183
+ const bootstrap = new Bootstrap({
184
+ root: window,
185
+ logger,
186
+ ioc: {
187
+ manager: IOC,
188
+ register: new IocRegisterImpl({ pathname, appConfig })
189
+ },
190
+ envOptions: {
191
+ target: appConfig,
192
+ source: import.meta.env,
193
+ prefix: 'APP_'
194
+ },
195
+ globalOptions: {
196
+ sources: globals,
197
+ target: 'AppGlobals'
198
+ }
199
+ });
200
+
201
+ // Register business logic plugins
202
+ bootstrap.use([
203
+ IOC(UserService), // User authentication service
204
+ new UserApiBootstarp(), // User API configuration
205
+ new FeApiBootstarp() // Other API configurations
206
+ ]);
207
+
208
+ // Start application
209
+ await bootstrap.initialize();
210
+ await bootstrap.start();
211
+ ```
212
+
213
+ **Comparison Results**:
214
+
215
+ - ✅ Component becomes cleaner, only responsible for rendering
216
+ - ✅ Business logic separated into bootstrapper
217
+ - ✅ Can independently test business logic
218
+ - ✅ Can reuse business logic in other UI frameworks
219
+
220
+ **Complete User Authentication Example**:
221
+
222
+ ```tsx
223
+ // 1. Define User API Plugin (Configure API requests)
224
+ export class UserApiBootstarp implements BootstrapExecutorPlugin {
225
+ readonly pluginName = 'UserApiBootstarp';
226
+
227
+ onBefore({ parameters: { ioc } }: BootstrapContext): void {
228
+ // Configure user API plugins
229
+ ioc
230
+ .get<UserApi>(UserApi)
231
+ .usePlugin(new FetchURLPlugin())
232
+ .usePlugin(IOC.get(IOCIdentifier.ApiMockPlugin))
233
+ .usePlugin(IOC.get(RequestLogger));
234
+ }
235
+ }
236
+
237
+ // 2. Define User Service (Handle user authentication logic)
238
+ @injectable()
239
+ export class UserService
240
+ extends UserAuthService<UserInfo>
241
+ implements ExecutorPlugin
242
+ {
243
+ readonly pluginName = 'UserService';
244
+
245
+ constructor(
246
+ @inject(UserApi) userApi: UserAuthApiInterface<UserInfo>,
247
+ @inject(IOCIdentifier.AppConfig) appConfig: AppConfig
248
+ ) {
249
+ super(userApi, {
250
+ userStorage: {
251
+ key: appConfig.userInfoStorageKey,
252
+ storage: storage
253
+ },
254
+ credentialStorage: {
255
+ key: appConfig.userTokenStorageKey,
256
+ storage: storage
257
+ }
258
+ });
259
+ }
260
+
261
+ // Check user authentication status before startup
262
+ async onBefore(): Promise<void> {
263
+ if (this.isAuthenticated()) {
264
+ return;
265
+ }
266
+
267
+ const userToken = this.getToken();
268
+ if (!userToken) {
269
+ throw new AppError('NO_USER_TOKEN');
270
+ }
271
+
272
+ await this.userInfo();
273
+ this.store.authSuccess();
274
+ }
275
+ }
276
+
277
+ // 3. Register in bootstrapper
278
+ const bootstrap = new Bootstrap({
279
+ root: window,
280
+ logger,
281
+ ioc: { manager: IOC, register: new IocRegisterImpl({ pathname, appConfig }) },
282
+ envOptions: { target: appConfig, source: import.meta.env, prefix: 'APP_' },
283
+ globalOptions: { sources: globals, target: 'AppGlobals' }
284
+ });
285
+
286
+ // Register user authentication related plugins
287
+ bootstrap.use([
288
+ IOC(UserService), // User authentication service
289
+ new UserApiBootstarp(), // User API configuration
290
+ IOC(I18nService) // Internationalization service
291
+ ]);
292
+
293
+ // 4. Start application
294
+ await bootstrap.initialize();
295
+ await bootstrap.start();
296
+ ```
297
+
298
+ **Key Points**:
299
+
300
+ - `UserApiBootstarp`: Responsible for configuring API requests
301
+ - `UserService`: Responsible for handling user authentication logic
302
+ - `Bootstrap`: Unified management of all plugins
303
+ - Components: Only responsible for UI rendering
304
+
305
+ And most crucially, UI and logic can exist independently, meaning you can implement multiple different UIs based on this logic.
306
+
307
+ **You might think using classes and objects is redundant, but in terms of software design and long-term project considerations, decades of development have shown us that this is the easiest pattern for developers to understand and collaborate on.**
308
+
309
+ ## Advantages of Bootstrapper
310
+
311
+ ### 1. **Testability**
312
+
313
+ ```tsx
314
+ // Can independently test business logic
315
+ describe('UserService', () => {
316
+ it('should authenticate user successfully', async () => {
317
+ const userService = new UserService(mockUserApi, mockAppConfig);
318
+ const result = await userService.onBefore();
319
+ expect(result).toBeDefined();
320
+ });
321
+ });
322
+ ```
323
+
324
+ ### 2. **Reusability**
325
+
326
+ ```tsx
327
+ // Same business logic can be used in different UI frameworks
328
+ // React version
329
+ const bootstrap = new Bootstrap({...});
330
+ bootstrap.use([IOC(UserService)]);
331
+
332
+ // Vue version
333
+ const bootstrap = new Bootstrap({...});
334
+ bootstrap.use([IOC(UserService)]); // Same business logic
335
+
336
+ // Native JS version
337
+ const bootstrap = new Bootstrap({...});
338
+ bootstrap.use([IOC(UserService)]); // Same business logic
339
+ ```
340
+
341
+ ### 3. **Extensibility**
342
+
343
+ ```tsx
344
+ // Easily add new business logic
345
+ bootstrap.use([
346
+ IOC(UserService),
347
+ IOC(PermissionService), // Add permission service
348
+ IOC(NotificationService), // Add notification service
349
+ IOC(AnalyticsService) // Add analytics service
350
+ ]);
351
+ ```
352
+
353
+ ### 4. **Team Collaboration**
354
+
355
+ - **Frontend Developers**: Focus on UI components and user experience
356
+ - **Backend Developers**: Focus on API interfaces and data processing
357
+ - **Architects**: Focus on business logic design and system architecture
358
+ - **Test Engineers**: Can independently test each module
359
+
360
+ ## Quick Start
361
+
362
+ If you want to quickly experience Bootstrap, follow these steps:
363
+
364
+ ### Step 1: Create Simple Plugin
365
+
366
+ ```tsx
367
+ // Create a simple plugin
368
+ export class SimplePlugin implements BootstrapExecutorPlugin {
369
+ readonly pluginName = 'SimplePlugin';
370
+
371
+ onBefore({ parameters: { logger } }: BootstrapContext): void {
372
+ logger.info('SimplePlugin started successfully!');
373
+ }
374
+ }
375
+ ```
376
+
377
+ ### Step 2: Register in Bootstrapper
378
+
379
+ ```tsx
380
+ const bootstrap = new Bootstrap({
381
+ root: window,
382
+ logger,
383
+ ioc: { manager: IOC, register: new IocRegisterImpl({ pathname, appConfig }) }
384
+ });
385
+
386
+ // Register plugin
387
+ bootstrap.use([new SimplePlugin()]);
388
+
389
+ // Start
390
+ await bootstrap.initialize();
391
+ await bootstrap.start();
392
+ ```
393
+
394
+ ### Step 3: Check Results
395
+
396
+ Open browser console, you'll see the log "SimplePlugin started successfully!".
397
+
398
+ **Tip**: This simple example shows the basic usage of Bootstrap. As you understand the project better, you can gradually add more complex business logic.
399
+
400
+ ## Real Project Application Scenarios
401
+
402
+ ### Scenario 1: Multi-platform Applications
403
+
404
+ ```tsx
405
+ // Web
406
+ const webBootstrap = new Bootstrap({
407
+ root: window
408
+ // Web-specific configuration
409
+ });
410
+ webBootstrap.use([IOC(UserService), IOC(WebSpecificService)]);
411
+
412
+ // Mobile H5
413
+ const mobileBootstrap = new Bootstrap({
414
+ root: window
415
+ // Mobile-specific configuration
416
+ });
417
+ mobileBootstrap.use([IOC(UserService), IOC(MobileSpecificService)]);
418
+
419
+ // Mini Program
420
+ const miniprogramBootstrap = new Bootstrap({
421
+ root: globalThis
422
+ // Mini program specific configuration
423
+ });
424
+ miniprogramBootstrap.use([IOC(UserService), IOC(MiniprogramSpecificService)]);
425
+ ```
426
+
427
+ ### Scenario 2: Micro-frontend Architecture
428
+
429
+ ```tsx
430
+ // Main application
431
+ const mainBootstrap = new Bootstrap({
432
+ root: window
433
+ // Main application configuration
434
+ });
435
+ mainBootstrap.use([IOC(GlobalUserService), IOC(RouterService)]);
436
+
437
+ // Sub-application A
438
+ const appABootstrap = new Bootstrap({
439
+ root: window
440
+ // Sub-application A configuration
441
+ });
442
+ appABootstrap.use([IOC(UserService), IOC(AppASpecificService)]);
443
+
444
+ // Sub-application B
445
+ const appBBootstrap = new Bootstrap({
446
+ root: window
447
+ // Sub-application B configuration
448
+ });
449
+ appBBootstrap.use([IOC(UserService), IOC(AppBSpecificService)]);
450
+ ```
451
+
452
+ ### Scenario 3: Progressive Upgrade
453
+
454
+ ```tsx
455
+ // Old version: Handle business logic directly in component
456
+ function OldApp() {
457
+ const [user, setUser] = useState(null);
458
+ useEffect(() => {
459
+ fetchUser().then(setUser);
460
+ }, []);
461
+ return <div>{user?.name}</div>;
462
+ }
463
+
464
+ // New version: Use bootstrapper to separate concerns
465
+ function NewApp() {
466
+ const { user } = useStore(); // Get from store managed by bootstrapper
467
+ return <div>{user?.name}</div>;
468
+ }
469
+
470
+ // Can coexist, migrate gradually
471
+ const bootstrap = new Bootstrap({...});
472
+ bootstrap.use([IOC(UserService)]); // New logic
473
+ // Old logic can still continue to be used
474
+ ```
475
+
476
+ ## Best Practices
477
+
478
+ ### 1. **Plugin Design Principles**
479
+
480
+ ```tsx
481
+ // ✅ Good plugin design
482
+ export class GoodPlugin implements BootstrapExecutorPlugin {
483
+ readonly pluginName = 'GoodPlugin';
484
+
485
+ // Single responsibility
486
+ onBefore({ parameters: { ioc } }: BootstrapContext): void {
487
+ // Do only one thing: configure API
488
+ ioc.get<UserApi>(UserApi).usePlugin(new FetchURLPlugin());
489
+ }
490
+ }
491
+
492
+ // ❌ Bad plugin design
493
+ export class BadPlugin implements BootstrapExecutorPlugin {
494
+ readonly pluginName = 'BadPlugin';
495
+
496
+ // Does too many things
497
+ onBefore({ parameters: { ioc } }: BootstrapContext): void {
498
+ // Configure API
499
+ ioc.get<UserApi>(UserApi).usePlugin(new FetchURLPlugin());
500
+ // Handle routing
501
+ ioc.get<RouterService>(RouterService).configure();
502
+ // Handle theme
503
+ ioc.get<ThemeService>(ThemeService).init();
504
+ // Handle internationalization
505
+ ioc.get<I18nService>(I18nService).load();
506
+ }
507
+ }
508
+ ```
509
+
510
+ ### 2. **Error Handling**
511
+
512
+ ```tsx
513
+ export class UserService implements ExecutorPlugin {
514
+ readonly pluginName = 'UserService';
515
+
516
+ async onBefore(): Promise<void> {
517
+ try {
518
+ if (this.isAuthenticated()) {
519
+ return;
520
+ }
521
+
522
+ const userToken = this.getToken();
523
+ if (!userToken) {
524
+ throw new AppError('NO_USER_TOKEN');
525
+ }
526
+
527
+ await this.userInfo();
528
+ this.store.authSuccess();
529
+ } catch (error) {
530
+ // Graceful error handling
531
+ this.store.authFailed(error);
532
+ this.routerService.gotoLogin();
533
+ }
534
+ }
535
+ }
536
+ ```
537
+
538
+ ### 3. **Performance Optimization**
539
+
540
+ ```tsx
541
+ // Conditional plugin loading
542
+ if (process.env.NODE_ENV === 'development') {
543
+ bootstrap.use([IOC(DevToolsService)]);
544
+ }
545
+
546
+ // Load plugins on demand
547
+ if (appConfig.features.analytics) {
548
+ bootstrap.use([IOC(AnalyticsService)]);
549
+ }
550
+ ```
551
+
552
+ ## Summary
553
+
554
+ The Bootstrap starter is not just a technical implementation, but an architectural concept. It helps us:
555
+
556
+ 1. **Separate Concerns**: Separate UI and business logic
557
+ 2. **Improve Maintainability**: Modular design, easy to understand and modify
558
+ 3. **Enhance Testability**: Each module can be tested independently
559
+ 4. **Support Team Collaboration**: Different roles can focus on their domains
560
+ 5. **Adapt to Changes**: Business logic changes don't affect UI, UI changes don't affect business logic
561
+
562
+ This design pattern is becoming increasingly important in modern frontend development, especially in large projects and team collaboration.