Software Design Architecture
Lộ trình phát triển toàn diện Software Design Architecture theo tiêu chuẩn quốc tế nilbuild/developer-roadmap
Hướng dẫn từng bước từ nền tảng đến chuyên sâu giúp bạn làm chủ Software Design Architecture. Tích hợp tài liệu lý thuyết, bài viết thực chiến, video tham khảo và bài tập lập trình trực tiếp trên IDE.
Nền Tảng & Khái Niệm Cốt Lõi
Giai đoạn 1 tập trung hoàn thiện 19 chủ đề then chốt.
Abstract Classes
An abstract class is a restricted base class that cannot be used to create objects. It serves as a blueprint for other classes, providing a common structure by defining methods that subclasses must implement. While it can contain fully functional methods, its primary purpose is to enforce a consistent interface and shared behavior across related objects in a system.
Abstraction
Abstraction is the process of hiding complex implementation details and showing only the essential features of an object to the user. It allows developers to interact with a simplified interface while the underlying logic remains encapsulated and separate. By focusing on what an object does rather than how it achieves its tasks, abstraction reduces programming complexity and prevents unnecessary exposure of internal operations.
Anemic Models
Anemic models are domain objects that contain little or no business logic, acting primarily as simple data holders with public getters and setters. In this design, the actual behavior and decision-making logic are placed in separate service classes, which interact with the data objects to perform operations. While this approach separates data from logic, it often leads to a procedural style of programming where the richness of the domain model is lost and the responsibility for maintaining data integrity is shifted to external services.
Architectural Patterns
Architectural patterns are high-level structural templates that shape the overall organization of a system, such as layered architecture, microservices, event-driven, and serverless. They define how major components communicate and scale, and are chosen based on the problem domain, team, and operational requirements.
Architectural Patterns
Architectural patterns are reusable solutions to common problems encountered during the high-level design of software systems. They provide a structural template that defines how components within an application interact, organize, and communicate. These patterns serve as a blueprint to ensure software remains scalable, maintainable, and efficient by establishing clear boundaries and responsibilities for different parts of the system. Common examples include Model-View-Controller, microservices, and layered architectures, each offering specific strategies for managing complexity and data flow.
Architectural Principles
Architectural principles are a set of fundamental guidelines and best practices used to inform the structural design of a software system. These rules provide a foundation for making consistent decisions regarding the organization of code, the interaction between components, and the management of data flow. By adhering to these concepts, developers can ensure that a system remains modular, scalable, and easier to maintain over time. These principles help teams establish a common technical language and prevent structural decay as an application grows in complexity.
Architectural Principles
Architectural principles refer to a set of guidelines or rules that are used to guide the design and development of a software architecture. These principles are intended to ensure that the resulting architecture is maintainable, scalable, and easy to understand and modify. Some common architectural principles include the separation of concerns, modularity, loose coupling, and high cohesion. Additionally, architectural principles are often used in conjunction with design patterns, which are reusable solutions to common software design problems.
Architectural Styles
Architectural styles are established approaches to organizing the high level structure of a software system, such as how components are distributed, how they communicate, and how they are deployed. Each style, like layered, client-server, or microservices, comes with its own trade-offs around scalability, maintainability, and complexity. Choosing a style shapes many later decisions in a project, including deployment strategy, team structure, and technology choices.
Architectural Styles
Architectural styles are high-level patterns that define the fundamental structure and organization of a software system. They provide a standardized way to arrange components, define their responsibilities, and establish the communication mechanisms between them. By selecting a specific style, such as microservices, layered architecture, or event-driven design, developers determine how data flows through the application and how the system scales over time. These styles serve as blueprints that guide technical decisions, ensuring the software remains modular, maintainable, and aligned with its functional requirements.
Avoiding Nulls and Booleans as Arguments
Passing null values or boolean flags into functions often leads to code that is difficult to understand and prone to errors. When a function accepts a boolean, it frequently signals that the function is doing too many things at once, violating the single responsibility principle. Similarly, passing null values forces developers to include repetitive conditional checks to prevent runtime crashes. Instead of using these markers, developers should decompose complex functions into smaller, specific methods or utilize specialized objects and patterns to handle different execution paths clearly.
Be Consistent
Being consistent refers to maintaining a consistent pattern. This can include using consistent naming conventions, data structures, and interfaces throughout the system, as well as adhering to established design principles and best practices. Consistency can help to make the system more maintainable, understandable, and extendable.
Blackboard Pattern
The Blackboard architectural pattern is a software design pattern that allows for the creation of a centralized repository of information that can be accessed and modified by multiple independent modules or subsystems. The blackboard serves as a communication and coordination mechanism between these modules, allowing them to share information and collaborate to achieve a common goal. This pattern is often used in artificial intelligence and decision-making systems, where multiple processes or agents need to share and reason over complex data.
Boundaries
Boundaries define the separation between different parts of a system, marking where one component's responsibility ends and another's begins. They control how data and control flow between modules, layers, or services, and they hide internal implementation details behind a stable interface. Clear boundaries let teams change the internals of one part of a system without affecting the others, which supports independent development and testing.
Class Variants
Class variants are a conceptual approach within object-oriented design used to represent different forms or specialized versions of an entity while maintaining a shared core identity. This technique involves defining a base structure that establishes common attributes and behaviors, which are then extended or modified to support specific functional requirements or domain constraints. By organizing classes into hierarchies or specific groupings based on these variations, developers can manage complexity and ensure that the system remains modular as new types of objects are introduced.
Clean Code Principles
Clean code principles are guidelines for writing code that is easy to understand, maintain, and modify. These principles emphasize readability, simplicity, and reducing complexity to improve collaboration and reduce the likelihood of errors. Ultimately, the goal is to create source code that is as easy to read and understand as well-written prose.
Clean Code Principles
Clean code refers to code that is easy to read, understand, and modify, following practices like meaningful naming, small functions, and clear structure. It prioritizes clarity over cleverness, since code is read far more often than it is written. Writing clean code reduces the time needed to fix bugs or add features, and it makes collaboration easier when multiple developers work on the same codebase.
Client Server
Client-server is a distributed architecture where clients request services or resources and servers provide them over a network. The client handles the user interface and user interaction, while the server manages data storage, business logic, and processing. This separation lets multiple clients share the same server resources and allows each side to be scaled or updated independently.
Command Query Separation
Command-Query Separation (CQS) is a software design principle that separates the responsibilities of a method or function into two categories: commands and queries. Commands are methods that change the state of the system, while queries are methods that return information but do not change the state of the system.
Commands Queries
The Command and Query Responsibility Segregation (CQRS) pattern separates operations that change a system's state, called commands, from operations that read data, called queries. A command performs an action such as creating or updating a record and returns no data, while a query returns data without producing any side effects. Keeping these two categories distinct clarifies the intent of each operation and makes it easier to scale or cache reads independently of writes.
Kỹ Năng Trọng Tâm & Thực Hành
Giai đoạn 2 tập trung hoàn thiện 19 chủ đề then chốt.
Component Based
Component-based architecture divides a system into independent, reusable components, each encapsulating a specific piece of functionality behind a well-defined interface. Components can be developed, tested, and replaced separately, as long as their interfaces stay stable. This approach supports reuse across different parts of an application or even across different projects.
Component Principles
Component principles are a set of guidelines used to organize software into cohesive, reusable, and decoupled modules. These principles address how to group classes and modules together to balance the competing needs of maintainability, flexibility, and independent deployability. By focusing on how components interact and evolve, developers can minimize the impact of changes and ensure that systems remain stable as they grow in complexity.
Composition over Inheritance
Composition over inheritance is a design principle that suggests achieving code reuse by assembling complex objects from simpler ones rather than inheriting behavior from a parent class. Instead of creating deep and rigid class hierarchies, this approach encourages developers to define small, focused components that can be combined or swapped out to build specific functionality. This method leads to more flexible and maintainable code because it reduces the tight coupling between classes and prevents the complications that arise from complex inheritance chains.
Concrete Classes
A concrete class is a blueprint in object-oriented programming that provides a complete implementation for all of its defined methods and properties. It serves as a tangible template that allows developers to create direct instances, or objects, which can then be utilized to perform tasks within an application. Unlike abstract classes or interfaces, a concrete class does not contain incomplete methods, ensuring that every defined behavior is fully operational and ready for immediate execution upon instantiation.
Coupling and Cohesion
Coupling measures how much one module depends on another, while cohesion measures how closely the responsibilities within a single module relate to each other. Low coupling and high cohesion make a codebase easier to change, since a module can be modified or replaced without breaking unrelated parts of the system. Tightly coupled modules tend to cause cascading changes across a codebase, while low cohesion spreads unrelated logic across a single module, making it harder to understand and test.
CQRS
Command Query Responsibility Segregation, or CQRS, separates the operations that change data, called commands, from the operations that read data, called queries, often using different models for each. This separation allows the read and write sides of a system to be optimized, scaled, and even stored independently. CQRS is often paired with event sourcing, since events naturally represent the commands that changed the system's state.
Design Patterns
Design patterns are general, reusable solutions to common problems that occur when designing software at the level of individual classes and objects. They describe proven approaches to structuring relationships between objects, such as how to create objects flexibly or how to let objects react to changes in other objects. The Gang of Four catalog, which includes patterns like Singleton, Factory, and Observer, is one of the most widely referenced sources for these patterns.
Design Patterns
Design patterns are reusable solutions to commonly occurring problems in software design. They represent best practices evolved over time by experienced software developers to address recurring challenges in structuring code. By providing a standardized vocabulary and template for solving structural or behavioral issues, these patterns help developers create systems that are more flexible, maintainable, and easier to understand.
Design Principles
Design principles are guidelines for writing clean, maintainable code — with SOLID, DRY, YAGNI, and composition over inheritance among the most well-known. Applied at the module and class level, they reduce coupling, improve readability, and make the codebase easier to extend and test.
Design Principles
Design principles are fundamental guidelines that help software engineers create systems that are maintainable, scalable, robust, and easy to understand. They represent best practices derived from decades of software engineering experience and are widely used to guide the structure and behavior of code. Applying these principles can lead to better software architecture, easier debugging, and improved collaboration.
Distributed
A distributed architecture spreads a system's components across multiple machines or networks that communicate to work as a single system. This approach improves scalability and fault tolerance, since the failure of one machine does not necessarily bring down the whole system. It also introduces challenges around network latency, data consistency, and coordination between nodes that a single-machine system does not face.
Domain Driven Design
Domain-Driven Design is an approach to software design that models the software's structure and language around the business domain it serves. It encourages close collaboration between developers and domain experts to build a shared vocabulary, called a ubiquitous language, that is reflected directly in the code. Concepts like entities, value objects, and bounded contexts come from this approach and help keep complex business logic organized.
Domain Language
Domain language refers to a specialized vocabulary and set of terms used by developers and domain experts to describe the specific concepts, processes, and rules of a business area. By establishing a shared vocabulary, it bridges the gap between technical implementation and business requirements, ensuring that the software design directly reflects the reality of the problem space. This approach relies on maintaining a consistent mapping between the code structure and the terminology used by stakeholders, which helps in creating a more intuitive and maintainable system.
Domain Models
A domain model is a conceptual representation of the real-world objects, entities, and processes that exist within a specific area of business or subject matter. It acts as a bridge between technical implementation and business requirements by documenting the relationships, attributes, and behaviors of core elements. Developers use these models to establish a common vocabulary and structure that ensures the software effectively reflects the logic of the problem it intends to solve.
Domain Models
A domain model represents the concepts, rules, and relationships of a business domain within the software, combining data and behavior into objects like entities and value objects. It captures business logic directly in these objects rather than spreading it across separate procedural scripts. A rich domain model makes it easier to express complex business rules clearly and keep them consistent as the application grows.
DRY (Don't Repeat Yourself)
DRY is a software development principle aimed at reducing the repetition of information and logic within a system. It suggests that every piece of knowledge or functionality should have a single, unambiguous representation within an application. By centralizing logic and avoiding redundant code, developers make systems easier to maintain, test, and update, as any necessary changes only need to be applied in one location.
DTOs
The Data Transfer Object Design Pattern is one of the enterprise application architecture patterns that calls for the use of objects that aggregate and encapsulate data for transfer. A Data Transfer Object is, essentially, like a data structure. It should not contain any business logic but should contain serialization and deserialization mechanisms.
Encapsulate what varies
Encapsulate what varies is a fundamental design principle that suggests identifying the parts of an application that are likely to change and separating them from the parts that remain constant. By isolating volatile code behind stable interfaces or abstractions, developers can modify or extend functionality without disrupting the rest of the system. This practice reduces the impact of future changes and makes the codebase easier to maintain, as updates are confined to specific, well-defined modules rather than spread across the entire architecture.
Encapsulation
Encapsulation is a fundamental principle of object-oriented programming that bundles data and the methods that operate on that data into a single unit, known as a class. It restricts direct access to an object's internal state by hiding its private implementation details from the outside world. Developers expose only the necessary functionality through a public interface, which helps prevent accidental corruption of data and ensures that the internal logic of an object remains consistent and protected.
Kiến Trúc Nâng Cao & Tối Ưu
Giai đoạn 3 tập trung hoàn thiện 19 chủ đề then chốt.
Enterprise Patterns
Enterprise patterns are reusable solutions to common problems found in business applications, particularly around organizing business logic and managing data access. They cover concerns such as how to represent business objects, how to move data between layers, and how to structure database access. Martin Fowler's book on enterprise application architecture documents many of these patterns, including data transfer objects, repositories, and the active record pattern.
Enterprise Patterns
Enterprise Patterns are well-documented, reusable solutions to commonly occurring problems in enterprise software development. These patterns provide a vocabulary and a set of best practices for designing robust, scalable, and maintainable enterprise applications, addressing recurring challenges like data access, concurrency, distribution, and integration with legacy systems. They serve as blueprints for solving complex architectural and design issues, promoting consistency and reducing development time.
Entities
An entity is an object defined by a unique identity that persists over time, even as its attributes change. Two entities with identical attributes are still considered different if they have different identities, unlike value objects. Entities typically represent core business concepts, such as a customer or an order, and carry the identity needed to track and reference them throughout the system's lifecycle.
Event Driven
Event-driven architecture structures a system around the production, detection, and consumption of events, which are records of something that happened. Components react to events asynchronously instead of calling each other directly, which reduces coupling between producers and consumers. This style suits systems that need to scale independently or respond quickly to changes, such as order processing or real-time notifications.
Event Sourcing
Event sourcing stores the state of an application as a sequence of events rather than storing only the current state. Each change to the system is captured as an immutable event, and the current state is derived by replaying those events in order. This approach provides a complete audit trail of every change and makes it possible to reconstruct past states, though it requires careful handling of event versioning and storage growth over time.
Functional Programming
Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It emphasizes the use of functions to solve problems, often using higher-order functions, immutability, and recursion. Instead of modifying data, functional programming creates new data structures.
GoF Design Patterns
GoF Design Patterns are a set of reusable solutions to common problems encountered during software design. Originally documented by the "Gang of Four" (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides), these patterns provide standardized approaches for structuring classes and objects to create flexible and maintainable systems. They are categorized into three main types: creational patterns, which handle object creation mechanisms; structural patterns, which simplify relationships between entities; and behavioral patterns, which focus on communication between objects. Using these patterns helps developers apply proven architectural concepts to solve complex programming challenges effectively.
Hollywood Principle
The Hollywood Principle is a design strategy that dictates that high-level components should control the flow of an application while low-level components are only called upon when needed. Instead of low-level modules actively polling or calling higher-level functions, the system informs the lower-level parts of the application when it is their turn to execute. This approach minimizes dependencies and prevents tight coupling, as lower-level modules do not need to know about the internal logic or the existence of the components that invoke them.
Identity Maps
An identity map is a pattern that keeps track of every object loaded from a database during a single unit of work, mapping each object to its unique identifier. When the same object is requested again, the identity map returns the existing instance instead of loading a new copy from the database. This avoids duplicate objects representing the same underlying data and helps maintain consistency when multiple parts of the code work with the same record.
Indentation and Code Style
Indentation and code style refer to the visual layout and formatting rules applied to source code to improve its readability and consistency. This involves standardizing the use of spaces or tabs for nesting, defining line lengths, and following naming conventions for variables and functions. By maintaining a uniform appearance across a codebase, developers can easily scan and understand the structure of the logic without being distracted by irregular spacing or inconsistent syntax patterns.
Inheritance
Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a new class to inherit the properties and methods of an existing class. The class that is inherited from is called the parent or super class, while the class that inherits is called the child or sub class. Inheritance enables code reuse and allows for a hierarchical organization of classes, where a child class can inherit the properties and methods of its parent class and potentially add or override them. The main advantage of inheritance is that it allows for a clean and organized way to reuse code and share functionality among classes.
Interfaces
An interface is a programming contract that defines a set of methods or properties that a class must implement without specifying how those members should function. It serves as a blueprint for classes, allowing developers to ensure that different objects adhere to a common structure regardless of their internal implementation. By decoupling the definition of behavior from the actual code, interfaces promote flexibility and allow for polymorphism, where different objects can be treated interchangeably as long as they satisfy the same interface requirements.
Keep framework code distant
Keeping framework code distant is a design practice that aims to decouple core business logic from the specific libraries or frameworks being used. It involves wrapping third-party tools within abstraction layers, such as interfaces or adapter classes, so that the application remains agnostic of the framework’s internal implementation. This approach ensures that if a framework needs to be updated, replaced, or removed, the impact on the codebase is minimized because the business rules are not directly tied to external dependencies.
Keep it simple and refactor often
"Keep it simple and refactor often" is a development philosophy that encourages writing straightforward, readable code and frequently improving its internal structure without changing its external behavior. This approach focuses on reducing complexity by removing redundant logic and breaking down large tasks into manageable pieces. Developers maintain high code quality by addressing technical debt incrementally, ensuring that the software remains easy to understand, test, and adapt to future requirements as it evolves.
Keep it Small
You should design and implement small, focused components that serve a specific purpose, rather than large, monolithic components that try to do everything. This can help to improve the maintainability and scalability of the system by making it easier to understand, test, and modify individual components.
Law of Demeter
The Law of Demeter is a design principle that restricts how objects interact with one another to reduce system coupling. It suggests that a given method should only call methods belonging to its own class, objects created within the method, objects passed as arguments, or direct component objects. By limiting these interactions to "only talk to your immediate friends," the principle ensures that a module does not need to know the internal structure or the navigation path of the objects it interacts with. This approach makes code easier to maintain and refactor because changes to the internal implementation of one object do not propagate across the entire system.
Layered Architectures
Layered architecture is a design pattern that organizes a software system into distinct horizontal segments or tiers, each with a specific responsibility. In this approach, components are grouped based on their function, such as presentation, business logic, or data access, with each layer communicating only with the one directly below it. By restricting dependencies in this manner, developers create a modular structure that makes it easier to update, test, and maintain individual parts of an application without affecting the entire codebase.
Layered
A layered architecture organizes a system into horizontal layers, such as presentation, business logic, and data access, where each layer only communicates with the layer directly below or above it. This separation makes it easier to reason about and test each layer in isolation. It is a common structure for traditional enterprise applications, though strict layering can add overhead when a request needs to pass through every layer.
Mappers
A mapper converts data between two different representations, such as translating a database row into a domain object or converting a domain object into a format suitable for an API response. This separation keeps the domain model free of persistence or transport concerns, since it does not need to know how it will be stored or transmitted. Mappers are often used alongside repositories and DTOs to keep each layer of an application focused on its own representation of data.
Hệ Sinh Thái & Triển Khai Thực Tế
Giai đoạn 4 tập trung hoàn thiện 19 chủ đề then chốt.
Meaningful Names
You should follow the practice of giving clear and descriptive names to different components of a system, such as variables, functions, and classes. This can help to make the system more understandable and maintainable by clearly communicating the purpose of each component and its intended usage.
Message Queues Streams
Message queues and streams are infrastructure components that store and pass messages between producers and consumers, allowing them to operate independently and at different speeds. A queue typically delivers each message to one consumer and removes it once processed, while a stream keeps an ordered log of events that multiple consumers can read and replay. Tools like RabbitMQ, Kafka, and Amazon SQS implement these patterns to support asynchronous, decoupled communication between services.
Messaging
Messaging is a way for different parts of a distributed system to communicate by sending and receiving discrete pieces of data called messages, instead of calling each other directly. It decouples the sender from the receiver, since the sender does not need to know who will process the message or when. Common messaging patterns include event-driven communication and publish-subscribe, both often implemented with message brokers like RabbitMQ or Kafka.
Microkernel
A microkernel is an architectural pattern in operating system design that aims to minimize the amount of code running in kernel mode (i.e., privileged mode with direct access to hardware resources) and instead move as much functionality as possible into user mode. This is done by providing a small, minimalistic core kernel that only handles basic tasks such as memory management, process scheduling, and inter-process communication (IPC), and leaving all other functionality to be implemented in user-mode processes.
Microservices
Microservices is an architectural pattern that is used to design software systems as a collection of small, independent, and loosely-coupled services. Each service is responsible for a specific functionality and can be developed, deployed, and scaled independently. The main advantage of a microservices architecture is that it allows for a more flexible and scalable system, it also improves fault isolation and enables faster deployment. It's often used in combination with other architectural patterns and styles such as event-driven architecture, CQRS, and service-oriented architecture.
Minimize Cyclomatic Complexity
Cyclomatic complexity measures the number of linearly independent paths through a program's source code. It's calculated based on the number of decision points (like `if`, `for`, `while`, `case`) within a function or code block. High cyclomatic complexity indicates code that is harder to understand, test, and maintain due to its increased number of possible execution paths. Reducing this complexity aims to simplify the control flow and logic within code, leading to more readable and reliable software.
Model-Driven Design
Model-Driven Design is a software development approach that emphasizes creating rigorous, conceptual models to represent the problem domain before writing any code. By focusing on these models, developers ensure that the software structure directly reflects the business logic and rules of the system. This method relies on using diagrams and formal specifications to define how objects interact and behave, which serves as the primary blueprint for the implementation. It allows teams to manage complex systems by keeping the design consistent and aligned with the requirements throughout the entire development process.
Model View Controller
Model-View-Controller (MVC) is an architectural pattern that separates the concerns of a software system into three distinct components: the model, the view, and the controller, where the model represents the data and the business logic of the system, the view represents the user interface of the system and the controller acts as an intermediary between the model and the view. The main goal of MVC is to separate the concerns of the system, making it easier to understand, maintain and evolve, it's widely used in web development.
Monolithic
A monolithic architecture builds an application as a single, unified codebase where all components, such as the user interface, business logic, and data access, run as one deployable unit. This simplifies development and deployment early on, since there is only one artifact to build and run. As the application grows, a monolith can become harder to maintain and scale, since a change in one part often requires redeploying the entire system.
Object Oriented Programming
Object-oriented programming (OOP) is a programming paradigm that is based on the concept of "objects," which are instances of a class. In OOP, a class is a blueprint for creating objects, which have both data (attributes) and behavior (methods). The main idea behind OOP is to model real-world objects and their interactions, making it well-suited for creating complex and large-scale software systems.
Object Oriented Programming
Object-oriented programming (OOP) is a programming paradigm that is based on the concept of "objects," which are instances of a class. In OOP, a class is a blueprint for creating objects, which have both data (attributes) and behavior (methods). The main idea behind OOP is to model real-world objects and their interactions, making it well-suited for creating complex and large-scale software systems.
Object Oriented Programming
Object-oriented programming (OOP) is a programming paradigm that uses objects and classes to structure and organize code. In OOP, an object is an instance of a class, which is a template that defines the properties and behaviors of the object. OOP is based on the principles of encapsulation, inheritance, and polymorphism.
Organize code by actor it belongs to
Organizing code by the actor it belongs to involves grouping components, classes, or modules based on the specific business entities or users they serve. This approach aligns the physical structure of a codebase with the actual responsibilities and requirements of the stakeholders involved in the system. By separating elements according to their primary actor, developers can minimize the impact of changes, as modifications requested by one group of users only affect the files associated with their specific domain. This practice reduces coupling and helps maintain a clear mapping between business processes and technical implementations.
ORMs
An Object-Relational Mapper, or ORM, translates data between a relational database and objects in an application's code, letting developers work with database records as regular objects instead of writing raw SQL. It handles tasks like generating queries, mapping rows to objects, and managing relationships between tables. Popular ORMs include Hibernate for Java, SQLAlchemy for Python, and Entity Framework for .NET, though they can introduce performance overhead compared to hand-written queries for complex operations.
Peer to Peer
Peer-to-peer is a distributed architecture where each node, called a peer, acts as both a client and a server, sharing resources directly with other peers instead of relying on a central server. This removes a single point of failure and can spread load across many machines. File-sharing networks and some blockchain systems use this style, though coordinating consistency and security across peers is harder without central control.
Policy vs Detail
Policy and detail represent a fundamental separation between the core logic of an application and the specific implementation mechanisms that support it. Policies define the high-level business rules, domain entities, and operational requirements that dictate what an application should accomplish. In contrast, details encompass the low-level components like database drivers, user interface frameworks, external APIs, and hardware configurations that provide the environment for those policies to run. Keeping these concepts separate ensures that business logic remains independent of the specific tools used to execute it, allowing developers to change or upgrade technical infrastructure without altering the core functionality.
Polymorphism
Polymorphism is a core principle in object-oriented programming that allows objects of different classes to be treated as instances of a common base class. It enables a single interface to represent different underlying forms, meaning a method call can behave differently depending on the specific object it is acting upon. By utilizing techniques like method overriding and overloading, developers can write flexible code that works with various data types through a unified structure.
PoSA Patterns
Pattern-Oriented Software Architecture (PoSA) patterns provide a standardized way to describe and organize recurring architectural structures and design solutions within complex software systems. These patterns function as proven blueprints that help developers structure the overall layout of an application, handle communication between components, and manage distributed systems. By offering a common vocabulary and set of templates for high-level design, PoSA patterns guide the process of decomposing a system into smaller, manageable parts that work together to meet functional and quality requirements.
Program against abstractions
Programming against abstractions involves interacting with interfaces or abstract base classes rather than concrete implementations. This approach allows a system to remain decoupled from the specific details of how a task is performed, enabling developers to swap or modify underlying components without affecting the rest of the application. By focusing on what an object does rather than how it is specifically constructed, code becomes more flexible, easier to maintain, and simpler to test.
Chuyên Gia & Mở Rộng Hệ Thống
Giai đoạn 5 tập trung hoàn thiện 18 chủ đề then chốt.
Programming Paradigms
A programming paradigm is a fundamental style or approach to structuring and writing code, such as object-oriented, functional, or procedural programming. Each paradigm offers different ways to organize logic, manage state, and handle control flow, and many languages support more than one paradigm. Understanding different paradigms helps developers choose the right approach for a given problem rather than forcing every solution into a single style.
Programming Paradigms
Programming paradigms are fundamental styles or approaches used to structure and organize computer programs. Each paradigm represents a distinct way of thinking about the execution of code, dictating how developers model problems and manage the state of an application. Common examples include procedural programming, which focuses on a sequence of instructions; object-oriented programming, which organizes code around data objects; and functional programming, which emphasizes the use of mathematical functions and immutable data. These approaches provide the conceptual framework that guides how software systems are designed and maintained.
Publish Subscribe
Publish-subscribe is a messaging pattern where senders, called publishers, send messages to a channel or topic without knowing who will receive them. Receivers, called subscribers, express interest in specific topics and receive only the messages published to those topics. A message broker sits between publishers and subscribers, routing messages and allowing multiple subscribers to receive the same message independently.
Pure Functions
Pure functions are a type of function in programming where the output is solely determined by its input values, without any side effects. This means that for the same input, a pure function will always return the same output, and it does not modify any state outside of its own scope. They are deterministic, predictable, and make code easier to test and reason about.
Repositories
A repository is a pattern that provides an abstraction over data storage, exposing methods to retrieve, add, update, or remove objects without exposing the details of how that data is persisted. Application code interacts with the repository's interface instead of writing database queries directly, which makes it easier to change the underlying storage technology or write tests using a fake implementation. Repositories are commonly paired with domain models in Domain-Driven Design.
Scope and Visibility
Scope and visibility define the accessibility of variables, methods, and classes within a codebase. Scope refers to the specific region of the program where a particular identifier is valid and can be referenced. Visibility, often managed through access modifiers like public, private, or protected, determines whether these members can be accessed from other parts of the application or restricted to specific classes and subclasses. Controlling these boundaries helps developers hide internal implementation details and prevent accidental modification of sensitive data.
Serverless Architecture
Serverless architecture lets developers build and run applications without managing the underlying servers, since a cloud provider automatically provisions, scales, and manages the infrastructure. Code runs in stateless functions that execute in response to events, and the provider charges based on actual usage rather than reserved capacity. This model reduces operational overhead but can introduce challenges around cold start latency and vendor lock-in.
SOA
Service-Oriented Architecture, or SOA, structures an application as a collection of loosely coupled services that communicate over a network, typically through standardized protocols. Each service exposes a well-defined interface and can be reused across different applications within an organization. SOA often relies on an enterprise service bus to manage communication, routing, and integration between services, which distinguishes it from the more independent, lightweight services used in microservices.
SOLID Principles
SOLID is a set of five design guidelines intended to make software designs more understandable, flexible, and maintainable. These principles include the Single Responsibility Principle, Open/Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, and Dependency Inversion Principle. Adopting these standards helps developers reduce code complexity, minimize tight coupling between components, and facilitate easier testing and future modifications. By applying these concepts, software systems become more robust and adaptable to evolving requirements.
Structural
Structural architectural styles focus on how a system's components are organized internally, such as how code is divided into layers, modules, or units. They determine how responsibilities are separated within a single application, as opposed to how the application communicates across a network. Common structural styles include monolithic, layered, and component-based design.
Structured Programming
Structured programming is a programming paradigm that emphasizes the use of well-structured control flow constructs such as loops, conditionals, and subroutines. It was developed in the 1960s and 1970s as a reaction to the "spaghetti code" produced by the widespread use of goto statements.
Tell, Don't Ask
Tell, don't ask is a principle that encourages object-oriented design where you tell an object to perform an action rather than asking for its internal state to make decisions yourself. Instead of retrieving data from an object using getter methods to perform logic externally, you encapsulate that logic within the object itself by providing a command or a method. This approach promotes better encapsulation by keeping the internal data hidden and ensuring that the object responsible for the data is also responsible for the behavior associated with it.
Tests should be fast and independent
Tests should be fast and independent refers to the practice of ensuring that a test suite executes quickly and that each individual test can run in any order without relying on the state of another. Fast tests provide immediate feedback during development, encouraging developers to run them frequently throughout the coding process. Independence ensures that the outcome of one test does not affect the results of others, which prevents cascading failures and makes it significantly easier to isolate and debug specific issues when a test fails.
Transaction Script
Transaction Script is a pattern used in enterprise application development that organizes business logic into a single procedural script. It is often used for simple CRUD (create, read, update, delete) operations, where all of the logic for a specific transaction is contained in a single script or function. This pattern is simple to implement and easy to understand, but can become unwieldy as the complexity of the application increases. Alternative patterns such as Domain-Driven Design (DDD) and the Active Record pattern may be more appropriate for more complex applications.
Use Correct Constructs
Using correct constructs involves selecting the most appropriate programming language features, data structures, and syntax to express a specific logic clearly and efficiently. This practice focuses on avoiding the misuse of language capabilities, such as using a loop when a built-in functional method is available or picking an inefficient data type for a simple task. By adhering to idiomatic patterns, developers create code that is predictable, easier to debug, and more readable for other team members.
Use Cases
A use case describes a specific interaction or process that a system supports to fulfill a user's goal, often expressed as a single class or function that orchestrates the steps needed to complete that goal. It sits between the presentation layer and the domain model, coordinating calls to entities, repositories, and other services without containing the core business rules itself. This separation keeps business logic testable and independent from how a request enters the system, whether through a web request, a command line, or an API call.
Value Objects
A value object is an object defined entirely by its attributes rather than by a unique identity, meaning two value objects with the same attributes are considered equal. Examples include a date range, a monetary amount, or a coordinate pair. Value objects are typically immutable, so once created, their values do not change, which makes them easier to reason about and share safely across a codebase.
YAGNI
YAGNI, which stands for "You Ain't Gonna Need It," is a principle in software development that suggests programmers should not add functionality until it is actually necessary. It encourages developers to focus only on the requirements at hand rather than attempting to predict future needs that may never arise. By avoiding the implementation of speculative features, the codebase remains simpler, easier to maintain, and less cluttered with unnecessary code.