Software Design Flashcards
(9 cards)
Domain-Driven Design (DDD)
Domain-driven design (DDD) is a major software design approach,[1] focusing on modeling software to match a domain according to input from that domain’s experts.[2] DDD is against the idea of having a single unified model; instead it divides a large system into bounded contexts, each of which have their own model.[3][4]
Under domain-driven design, the structure and language of software code (class names, class methods, class variables) should match the business domain. For example: if software processes loan applications, it might have classes like “loan application”, “customers”, and methods such as “accept offer” and “withdraw”.
https://en.wikipedia.org/wiki/Domain-driven_design
Strategic Design in DDD is a set of principles and practices focused on:
Identifying and defining the business Core Domains and Subdomains.
Establishing clear boundaries between different parts of the system (Bounded Contexts).
Developing a shared language (Ubiquitous Language) within each Bounded Context.
Mapping relationships between different Bounded Contexts and Teams (Context Mapping).
Aligning the software architecture with the business strategy and domain structure.
The DDD emphasizes the collaboration between Domain Experts and Software Developers, recognizing that neither can fully comprehend the complexities of the domain alone.
A common mistake is using the domain model, including aggregates, for querying purposes. The domain model is primarily designed for handling commands, and you should avoid complex queries that may hinder system performance. For queries, it’s better to execute them directly against repositories or explore techniques like building Read Models.
What is the advantage of Bounded Contexts
The advantage of those Bounded Contexts is that you develop the software completely separately… with custom software linked to standard packages. And when a certain Bounded Context is outdated, you can replace it with a new one, without changing the rest and everything continues to run.
The classic example is an ecommerce website that has an Order model, with all of the line items, price, tax, discounts, delivery address, etc. Once this Order model hits the warehouse boundary it takes on another meaning and purpose. People in the warehouse don’t care about pricing, tax and discounts. They care about a Packing Slip, with product SKUs, shelf locations, shipping details, etc.
Aggregates
An Aggregate in DDD is a cluster of related objects that are treated as a single unit.
Aggregates are the basic element of transfer of data storage – you request to load or save whole aggregates. Transactions should not cross aggregate boundaries.
A common misconception is to design Aggregates based on hierarchy and relationships between entities. However, the primary focus should be on behaviors and the invariants you need to enforce within the Aggregate. The data that drives these invariants is what matters most, and it should be based on the behaviors being exposed. Building an object model hierarchy without considering the behaviors can lead to unnecessary complexity.
Composite Table vs Join
Joins are query-time operations that combine data from multiple normalized tables without permanently altering the structure of the data.
Composite Tables are pre-built, stored tables that contain data merged from multiple sources—trading off some normalization (and potential data redundancy) for faster, simpler queries.
Invariants
Invariants are the rules that must be maintained within the Aggregate to ensure data integrity.
ACID transactions
In transaction processing, ACID (atomicity, consistency, isolation, and durability) is an acronym and mnemonic device used to refer to the four essential properties a transaction should possess to ensure the integrity and reliability of the data involved in the transaction.
Atomicity. A transaction is treated as a single atomic unit. All steps that make up the transaction must succeed or the entire transaction rolls back.
Consistency. A transaction must preserve the consistency of the underlying data. The transaction should make no changes that violate the rules or constraints placed on the data.
Isolation. A transaction is isolated from all other transactions. Transactions can run concurrently only if they don’t interfere with each other.
Durability. A transaction that is committed is guaranteed to remain committed – that is, all changes are made permanent and will not be lost if an event such as a power failure should occur.
CI/CD
CI/CD or CICD is the combined practices of continuous integration (CI) and continuous delivery (CD) or, less often, continuous deployment.
They are sometimes referred to collectively as continuous development or continuous software development.
What is Markdown?
Markdown is a lightweight (minimal) markup language for creating formatted text using a plain-text editor.
See this on Wikipedia
Functional Requirements vs Non-Functional Requirements
While functional requirements specify what a software product should do (for example, “users must be able to log in”), non-functional requirements define how well it must accomplish these tasks under real-world conditions (for example, “the login process should respond within two seconds under peak load” or “all user credentials must be encrypted and stored securely”).
NFRs are essential for the following reasons:
Quality of Service: NFRs like response time, availability, and usability directly affect the user’s perception of quality. A system that fulfills its functional requirements but is slow, constantly crashes, or is difficult to use can undermine user trust and satisfaction.
System Stability: Requirements such as reliability, fault tolerance, and recoverability help maintain stable operation even when part of the system fails. Without these, unhandled errors can escalate into large-scale outages.
Security and Compliance: Security-related NFRs dictate how data is protected, how access is controlled, and how audits are conducted. Neglecting these can lead to breaches, legal consequences, or reputational damage.
Scalability and Performance: Requirements for throughput, capacity, and resource utilization ensure the software can handle growth in users or data. If not addressed from the start, scaling can become prohibitively expensive or technically challenging later on.
Maintenance and Evolution: Maintainability, testability, and modularity requirements determine how easily bugs can be fixed, features added, or adaptations made to changing environments. Overlooking them can lead to ballooning technical debt, slowing down future development.
In short, non-functional requirements are not mere “nice-to-haves” but essential components that ensure a software system truly meets user expectations and withstands real-world challenges.