From e3f33f4fdf30919314042f488109ce9d92a03661 Mon Sep 17 00:00:00 2001 From: Tommaso Allevi Date: Sun, 17 Nov 2024 17:51:45 +0100 Subject: [PATCH] Fix Why --- packages/website/src/pages/Why.md | 39 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/website/src/pages/Why.md b/packages/website/src/pages/Why.md index 7a0555f..30f902b 100644 --- a/packages/website/src/pages/Why.md +++ b/packages/website/src/pages/Why.md @@ -4,7 +4,7 @@ As a developer, I worked with many technologies, mainly on the backend. While fa I started using Angular and React and have used them for many years. Anyway, every time I tried to build something more complex, the frameworks and libraries introduced a lot of complexity and cognitive stress. Instead of focusing on the business logic, I used the framework or the library. I didn't realize or understand that my confusion had been transformed into frustration. -It took me a while to understand it, but I realized that the existing frameworks and libraries are only sometimes the best solution for the problem I have to solve. I realized that the main products I must build are digital products, like websites and web applications, where the users land on it only after authentication. The stakeholder focuses on the user experience. Narrowing my focus on those types of products, I started to think about what I liked and didn't like about the frontend world: +It took me a while to understand it, but I realized that the existing frameworks and libraries are only sometimes the best solution for the problem I have to solve. I realized that I must build digital products, like websites and web applications, where users land on them only after authentication. The stakeholder focuses on the user experience. Narrowing my focus on those types of products, I started to think about what I liked and didn't like about the frontend world: - While my code organization at the back end follows a domain-driven design, my front-end code is structured differently. Consequently, the code is organized in a way that is only sometimes clear about its purpose: no framework or library suggested anything on this topic. - SSR, SSG, ISC, and other techniques are helpful. The main project I followed was related to a website under a login where SEO is not essential. Instead, the application exposes some Rest APIs that a front-end consumes, like a typical SPA. So, the mentioned technologies aren't valuable for this use case. - Node.js and similar technologies have become famous because the development team can use the same language on both sides: the team can share code between the front-end and back-end. Anyway, the portion of the shared code could be higher: sharing little pieces of code like validation rules avoids code duplication and improves the quality of the product. @@ -13,22 +13,21 @@ I have also tried different frameworks but have yet to find someone to resolve a ## What I was looking for -I appreciate the innovation and diversity of the frontend world, which brings me here: I want to find a way to simplify my work, reduce complexity, and use language features almost everywhere. I want to use a framework that helps me build digital products focused on simplifying user experience improvement. +I appreciate the innovation and diversity of the frontend world. This brings me here: I want to find a way to simplify my work, reduce complexity, and use language features almost everywhere. I want to use a framework that helps me build digital products focused on simplifying user experience improvement. From this point, I started thinking about what I liked and didn't like about the front-end world. The output of this thinking was the following list: - I like the component-based architecture, which allows the UI to split into small, reusable, and independent pieces. It enables the team to build a design system and reuse the components across the application (or applications). - I prefer that the project structure of the application logic follows the team logic without putting any constraints on it. So, class, function, and enum usages are possible everywhere. - I love the async/await usage: it is a way to handle the asynchronous code. In the past, the front-end code used callbacks after promises, but now, all browsers can use the async/await language feature. -- Typescript support reduces errors and improves code quality. Even if only some people like it, it is an excellent tool for enhancing code quality and helping other developers understand and contribute to it. +- Typescript support reduces errors and improves code quality. Even if only some people like it, it is an excellent tool for enhancing code quality and helping other developers understand and contribute. - The simplicity of integrating into other frameworks and supporting multiple versions on the same page without strange tricks. ## The solution I thought So, starting from here, I try to build something new, keeping in mind the following key concepts: -- Simplicity over Complexity -- Events over State Management -- Linearity over Complex Abstractions -- Explicitness over Implicitiveness +- Explicitness over Implicitiveness: I want to know what the code does without remembering how the framework lifecycle works. +- Events over State Management: I want to listen to and react to events instead of managing the state. +- Async/Await over Complex Abstractions: I want to use the async/await syntax to handle the asynchronous operations. With this in mind, I started to write the `SeqFlow` framework. @@ -39,7 +38,7 @@ The following are some decisions I took and on which the framework is built. I started to think about a way to simplify the component rendering, and I thought about the following requirements: - The component should be a function, not a class. This is because the class introduces a lot of boilerplate code, and it is not always clear which methods are invoked and when. Instead, the function exposes just one way to be invoked: the function invocation. - The component should easily process asynchronous operations, like data fetching, and the rendering should be updated when the data is available. -- As a developer, I read the code from top to bottom without jumping from the bottom to the top. This should also be true for the front-end components. This implies there's no re-rendering of the whole component when the state changes: if something changes, the code explicitly updates the whole component or a part of it. +- As a developer, I read the code from top to bottom as it is executed. This should also be true for the front-end components. This implies that the whole component is not re-rendering when the state changes: if something changes, the code explicitly updates the component or a part of it. From this list, I structured components as asynchronous functions, where the developer can use the async/await syntax to handle the asynchronous operations. @@ -51,7 +50,6 @@ export async function MyComponent({}, {component}: Contexts) { const userData: { username: string, - name: string, } = await fetchUserData(); // async function component.renderSync(

Hi, {username}!

); @@ -70,9 +68,9 @@ import { Contexts } from "@seqflow/seqflow"; export async function Counter({}, {component}: Contexts) { let counter = 0; - this.renderSync(); + this.renderSync(); - const events = component.waitEvents(component.domEvent(component._el, "click")); + const events = component.waitEvents(component.domEvent("click-me-button", "click")); for await (const ev of events) { counter++; window.alert(\`Clicked \${counter} times\`); @@ -84,11 +82,11 @@ You can listen more events at the same time. ### Domains -The more the features are added, the more the complexity is introduced. To reduce the noise, `SeqFlow` uses the concept of domains. A domain is, shortly, a way to split the applications into parts, segregated by logic boundaries. Inside a domain, the application logic classes/functions are stored, and the components that refer to it are stored there: in this way, the code is structured into folders that match the business domains. For instance, the `AddProductToCart` component is stored in the `Cart` domain, and the `UserBadge` component is stored in the `User` domain. +The more the features are added, the more the complexity is introduced. To reduce the noise, `SeqFlow` uses the concept of domains. A domain is, shortly, a way to split the applications into parts, segregated by logic boundaries. Inside a domain, the application logic classes/functions are stored, and the components that refer to it are stored there: in this way, the code is structured into folders that match the business domains. For instance, the `AddProductToCart` component is stored in the `Cart` domain, meanwhile the `UserBadge` component is stored in the `User` domain. This structure reduces the complexity and the cognitive stress because if a feature is needed, the team knows where to implement it and how it interacts with the other domains. -In `SeqFlow`, the suggested structure is the following: +We suggest the following structure: ``` src/ @@ -150,16 +148,16 @@ export async function UserBadge({}, { component, app }: Contexts) { } ``` -Now, the `UserBadge` is listening to the `UserLoggedEvent` event, and every time the event is emitted, the component automatically updates the badge. -So, when the user logged in, the login form emits the `UserLoggedEvent` event. +Now, the `UserBadge` is listening to the `UserLoggedEvent` domain event, and every time the event is emitted, the component automatically updates the badge. +When the user logs in, a component emits the `UserLoggedEvent` event, and the `UserBadge` component automatically updates the badge. -See the E-Commerce example for more information about it. +See the E-Commerce example for more information about it. -NB: Even if `SeqFlow` doesn't match the E-Commerce use case due to the SEO optimization, it is still a good example because there are different domains to connect. We developed it as an example to show how the structure is organized. +NB: Even if `SeqFlow` doesn't match the E-Commerce use case due to the SEO optimization, it is still a good example for tutorial reasons because there are different domains to connect. We developed it as an example to show how the structure is organized. ### No global injection -`SeqFlow` instance isn't installed globally. This means it lives where it is used, and it is not shared with other instances of SeqFlow nor other libraries. This is a way to reduce the complexity when the application has to be integrated with other libraries and frameworks, like Custom Components. +`SeqFlow` instance isn't installed globally. This means it lives where it is used, and it is not shared with other instances of SeqFlow or other libraries. This is a way to reduce the complexity when the application has to be integrated with other libraries and frameworks, like Custom Elements. ```ts import { start } from "@seqflow/seqflow"; @@ -185,5 +183,6 @@ This allows the developer to stop the `SeqFlow` instance and free the resources ## Conclusion -`SeqFlow` is thouth to be a way to simplify the frontend development, reducing the complexity and the cognitive stress. -In fact, leveraging on standards, the API shouldn't be so obscure. +`SeqFlow` is thought to be a way to simplify front-end development, reducing complexity and cognitive stress. + +Feel free to try it and give feedback by opening an issue on the [GitHub repository](https://github.com/allevo/seqflow-js/issues). We are open to suggestions and improvements.