Tale of Software Architect(ure): Part 4 (Things Should Consider When Design/Architect a Software System)
Saiful Islam Rasel
Senior Engineer, SDE @ bKash | Ex: AsthaIT | Sports Programmer | Problem Solver | FinTech | Microservice | Java | Spring-boot | C# | .NET | PostgreSQL | DynamoDB | JavaScript | TypeScript | React.js | Next.js | Angular
Story:
In a busy town, there’s a popular online bakery run by a system of various components working together to ensure smooth operations. Each component has a special job, but they must interact each other to keep the bakery running efficiently.
At the heart of the bakery is data, like the ingredients for the bakery’s recipes. There’s data about customers, orders, inventory (flour, sugar, etc.), and payments. The bakery’s system stores this data in neat databases: one for customers, one for products, and one for orders. These databases are like the pantry, where everything is well-organized so the bakers can grab what they need quickly.
Whenever a customer places an order, the system must follow a precise recipe, where all steps must succeed together. This is a transaction, just like baking a cake where you can’t skip steps.
Behind the scenes, sometimes the interactions are synchronous—like when the baker asks the delivery driver, “Is the van ready?” and waits for a “Yes” or “No.” Other times, they are asynchronous—like when the baker sends a note to the kitchen to start baking and doesn’t wait for an immediate response, knowing the kitchen will get to it when they can.
Key Factor Consideration when Start Designing/Architecting a Software System:
1. Data
Data is often the most critical asset in a software system, as most applications revolve around storing, processing, and retrieving data. Managing how data is structured, stored, accessed, and transmitted plays a central role in the architecture.
Key Considerations for Data:
Types of Data Need to Handle:
2. Transactions
Transactions are essential for maintaining data integrity, especially in systems where multiple operations are performed on the same set of data. In distributed systems or microservices architectures, managing transactions becomes even more challenging.
Key Considerations for Transactions:
3. Components
In software architecture, a component is a modular, self-contained unit that performs a specific function within a larger system. It is like a building block, with a defined role and a clear boundary, often encapsulating a set of related functionalities. Components interact with other components through well-defined interfaces or APIs to contribute to the overall functionality of the system.
Key Considerations of Components:
4. Component Boundaries
Component boundaries refer to the clear lines of separation between individual components in a software system. These boundaries define what is inside a component (its internal logic, data, and functionality) and what is exposed to the outside world (its interfaces or APIs). Component boundaries are critical because they enforce encapsulation, manage interactions, and ensure that components remain modular and maintainable.
Key Considerations of Component Boundaries:
Encapsulation:
Defined Interfaces:
Loose Coupling:
Responsibility Separation:
Boundary as a Contract:
5. Interaction Between Components
In any complex software system, different components must interact efficiently and reliably. These components may be individual modules, microservices, databases, or external APIs.
Key Considerations for Component Interaction:
Coupling and Cohesion: One of the principles of good architecture is to reduce coupling (interdependence) between components while increasing cohesion (internal consistency within a component). Loosely coupled components are easier to maintain and extend, as changes to one component have minimal impact on others.
Communication Patterns: How components communicate with each other can greatly affect system performance and reliability:
State Management: If components share state, it’s important to ensure consistency. In distributed systems, eventual consistency models may be required where state synchronization happens over time.
APIs and Contracts: Each component should expose a clear API or interface that defines how other components can interact with it. These contracts should be stable to ensure that changes in one component do not break others.
Service Discovery: In a dynamic environment (e.g., microservices), components may be distributed across multiple servers or containers. Service discovery mechanisms allow components to find and communicate with each other without hard-coding addresses.
Load Balancing and Fault Tolerance: If one component needs to interact with another, load balancing ensures that requests are spread out to avoid overloading any single instance. Fault tolerance ensures that if a component fails, the system can recover gracefully or reroute requests to a backup instance.
Others Important Thinking Parts:
Keeping the above key factors in mind we should consider or drive the following things while start designing a software system.
1. Understand Requirements (Functional and Non-Functional)
Key Considerations:
2. Modularity and Separation of Concerns
Key Considerations:
3. Scalability
Key Considerations:
领英推荐
4. Maintainability and Extensibility
Key Considerations:
5. Performance and Efficiency
Key Considerations:
6. Security
Key Considerations:
7. Data Management and Storage
Key Considerations:
8. Reliability and Fault Tolerance
Key Considerations:
9. Concurrency and Parallelism
Key Considerations:
10. User Experience (UX) and Usability
Key Considerations:
11. Technology Stack Selection
Key Considerations:
12. Testing Strategy
Key Considerations:
13. Deployment Strategy
Key Considerations:
14. Documentation
Key Considerations:
15. Cost Efficiency
Key Considerations:
Summary:
In software architecture, several key concepts play a crucial role in building robust systems. Data is the core asset, representing all the information the system processes, while transactions ensure data consistency and integrity by grouping operations into all-or-nothing units. Interactions are the communication between components that enable collaboration to achieve system functionality, either synchronously or asynchronously.
A component is a modular, self-contained unit responsible for a specific task, designed to be reusable, maintainable, and encapsulated. Component boundaries define the separation between components, ensuring that internal logic is hidden and interactions occur only through well-defined interfaces. These boundaries promote loose coupling and clear responsibility separation, making the system more scalable, maintainable, and adaptable.