Next Article in Journal
Diabetes Prediction and Detection System Through a Recurrent Neural Network in a Sensor Device
Previous Article in Journal
Inverse-Time Overcurrent Protection Scheme for Smart Grids Based on Composite Parameter Protection Factors
Previous Article in Special Issue
PointFuzz: Efficient Fuzzing of Library Code via Point-to-Point Mutations
 
 
Font Type:
Arial Georgia Verdana
Font Size:
Aa Aa Aa
Line Spacing:
Column Width:
Background:
Article

Root Contracting: A Novel Method and Utility for Implementing Design by Contract in Domain-Driven Design with Event Sourcing

by
Chien-Tsun Chen
1,2,
Yi-Chun Yen
2,
Yu-Hsiang Hu
2 and
Yu Chin Cheng
2,*
1
Teddysoft, Taipei 100001, Taiwan
2
Department of Computer Science and Information Engineering, National Taipei University of Technology, Taipei 106344, Taiwan
*
Author to whom correspondence should be addressed.
Electronics 2025, 14(21), 4205; https://doi.org/10.3390/electronics14214205
Submission received: 24 September 2025 / Revised: 23 October 2025 / Accepted: 23 October 2025 / Published: 28 October 2025
(This article belongs to the Special Issue Software Engineering: Status and Perspectives)

Abstract

Event-Sourced Systems (ESSs) that adopt Domain-Driven Design (DDD) are becoming more popular because of their intuitive business process modeling and improved auditability, scalability, and flexibility. However, ensuring the correctness of domain models—particularly event-sourced aggregates (ESAs)—remains challenging. To address this, we propose root contracting, a novel, constrained, and lightweight adaptation of Design by Contract (DbC), specifying the precondition, postcondition, and class invariant exclusively at aggregate roots. Root contracting simplifies correctness enforcement by leveraging DDD principles aligned with DbC and the standardized ESA code structure. We offer uContract, a Java open-source utility that realizes root contracting, enabling automated verification of ESAs with configurable runtime overhead. Through performance evaluation and methodological discussion, we demonstrate that root contracting effectively bridges formal correctness with practical domain modeling. Our approach provides developers with a tool to streamline development workflows, potentially reducing testing overhead and supporting integration with methodologies like Behavior-Driven Development (BDD).

1. Introduction

Event-Sourced Systems (ESSs) are gaining popularity because they facilitate a more intuitive representation of business processes while also enhancing auditability, scalability, and flexibility [1]. A recent survey of 19 ESSs identified Domain-Driven Design (DDD) as the predominant design approach applied [2]. The co-occurrence is not surprising, as events in ESSs coincide with Domain Events of DDD, both referring to “something happened [in a business process] that domain experts care about.” [2,3]. Furthermore, DDD provides a pattern language for designing complex systems and is well-suited for building ESSs. DDD’s strategic and tactical patterns help teams define bounded contexts, establishing Aggregates to ensure integrity by enforcing encapsulation and transactional boundaries. When combined, DDD and Event Sourcing reinforce each other by improving correctness and auditability within message-oriented architectures such as microservices [4].
In this combination, aggregates in ESSs, referred to as event-sourced aggregates or ESAs for short, constitute the foundation for ensuring the correctness of ESSs. To quote Eric Evans, “We keep the aggregates internally consistent at all times, while making other changes asynchronously.” [3]. When an aggregate stores a domain event, it must ensure that the domain event correctly reflects the cause of change and that the aggregate is in a valid state.
This paper focuses on ensuring the correctness of ESAs. We demonstrate that combining DDD and Event Sourcing produces a design that enables a practical and sustainable application of Design by Contract (DbC) [5] on ESAs. The significance of this enablement lies in the observation that, although DbC is a proven method for building correct and robust software systems in Eiffel—the best-known language to support DbC natively—its adoption is more of an exception than the norm outside Eiffel. In the other languages, DbC is mostly used at the analysis and design levels due to the lack of language support [6,7,8]. Software developed in popular languages like Java, C#, and Go can use third-party DbC extensions for DbC support [9,10,11,12,13,14,15,16,17,18,19,20,21]. These extensions can introduce modifications to host languages and compilers and may require additional tooling, such as contract compilers, preprocessors, postprocessors, aspect-oriented programming [10,17], and bytecode instrumentation [18,22] (Table 1). However, development of most DbC extensions has been discontinued or become dormant, indicating that it is rare for the existing DbC extensions to keep up with host language and toolchain evolutions. This, in turn, discourages developers from adopting DbC.
By leveraging the DDD–Event Sourcing design, we propose a constrained form of DbC for ESSs called root contracting, which ensures the correctness of ESAs without the need for a DbC extension. In a nutshell, root contracting checks the aggregate invariant (per Aggregates pattern [6] (pp. 128–129)) and the precondition and postcondition of commands (per Assertions pattern [6] (pp. 255–256)). In root contracting, contracts are specified only at the aggregate roots, rather than across all internal objects of ESAs. Furthermore, we offer uContract, an open-source Java utility library that supports this approach.
The proposed root contracting and the supporting utility library uContract have the following practical benefits:
  • Contracting with a simple utility. Using a utility library like uContract, which does not alter the host language, allows root-contracted programs to compile directly without pre- or post-processing. This method simplifies adapting to changes in the host language compared to existing DbC extensions.
  • Collocated contracts and targets. Root contracting inlines assertions of an aggregate’s properties as code segments that are collocated with the targets that the assertions verify in the program text.
  • Contracts as source code. Contracts are written in standard programming constructs understood by compilers, unlike annotations or comments in some DbC extensions, which are not checked by compilers.
  • Comprehensive support for contract evaluation and monitoring. Contracts can be activated or deactivated as recommended by DbC in the development, testing, or production phases.
  • Economy of contracts. Contracts apply only to aggregate roots; objects within aggregates are not subject to contracts. Therefore, contract management and maintenance focus exclusively on aggregate roots.
The rest of this paper is organized as follows. Section 2 reviews the historical attempts and challenges of adopting DbC in object-oriented languages and discusses the motivation of root contracting, a constrained form of DbC, based on a common ESA code structure. Section 3 adapts DbC correctness rules for ESAs and presents root contracting. Section 4 presents the design of uContract. Section 5 presents experiments designed to measure uContract’s performance overhead in various contract monitoring configurations. It further focuses on checking only the precondition—a good practice even without using DbC—to compare uContract’s overhead with that of a Java-native implementation. Section 6 compares uContract with other DbC tools across various aspects. Section 7 discusses how root contracting can contribute to correctness, robustness, and applicability compared to comprehensive DbC with DbC extensions. Section 8 concludes the paper and discusses possible future work.

2. Background and Motivation

This section provides a historical overview of DbC and discusses reasons for its limited adoption beyond Eiffel (Section 2.1). We then examine the design and code structure of ESAs created using DDD and Event Sourcing, which serve as the foundation of the proposed root contracting (Section 2.2). Finally, we discuss the circumstances that motivated root contracting (Section 2.3).

2.1. A Historical Perspective on DbC and DbC Extensions

Introduced by Bertrand Meyer in the 1980s, Design by Contract (DbC) is a well-known approach for creating reliable software that includes the core elements of contract specification, contract monitoring, and subcontracting [5,23]:
  • Contract specification refers to the language constructs used to write the precondition, postcondition, and class invariant. Contracts are assertions that define the obligations and benefits of a software supplier and its client. The precondition establishes the client’s obligations, while the postcondition states the guarantees provided by the supplier. The class invariant defines properties that each instance of a class must maintain before and after any invocation of its methods.
  • Contract monitoring is the mechanism to configure runtime contract checking. Because executing contracts requires additional computation, software developers and operators should have the option to enable or disable specific types of contracts at runtime. For example, they can activate every type of contract during development and only keep the precondition enabled during operation.
  • Subcontracting involves applying contracts within an inheritance hierarchy. Specifically, a derived class’s contracts are determined according to the subcontracting rules: an overriding method’s precondition is combined with its overridden method’s precondition using logical OR, and the postcondition with logical AND; a derived class’s class invariant is combined with that of the base class using logical AND.
For decades, Eiffel has remained the only DbC-native language that fully supports DbC. Eiffel employs the reserved words require, ensure, and invariant to specify the precondition, postcondition, and class invariant, respectively. Additionally, the reserved words old and Result are used in the postcondition to refer to a value before the execution of a method body and to represent the method’s return value, respectively. Contract monitoring is supported through Eiffel environment variables. Finally, where inheritance occurs, contracts of a derived class are derived according to the subcontracting rules outlined above.
Outside the Eiffel environment, these core elements are implemented through extensions to the host languages and their toolchains. Many efforts have been made to support DbC with DbC extensions. We notice that traditional DbC extensions aim for an ambitious goal: to support DbC as thoroughly as possible. As a result, the costs of development and maintenance to keep DbC extensions aligned with frequent host language updates are very high, leading to the discontinuation or dormancy of most of these extensions, which discourages developers from using DbC.
The problem with using a DbC extension is clear in Java and C#. In the case of Java, DbC support mostly comes through community-developed extensions. Despite many efforts [9,10,11,12,13,14,15,16,17,18,19], only JML [14,15,16] (now OpenJML) still receives some maintenance activities [24]. In the case of C#, Code Contracts provided good support in .NET 4 but was discontinued after .NET 5 [25]. A software development organization using Code Contracts would either need to stick with .NET 4 or bear the cost of migrating to a substitute for contracts. Neither option is ideal.

2.2. A Pattern-Based Design of Event-Sourced Aggregates

An ESA’s design is influenced by patterns including Domain Events, Aggregates, and Event Sourcing. Therefore, it must address the individual problems these three patterns set out to solve. This section presents a pattern-based design for ESAs, drawing on previous results from the existing literature [26,27,28,29]. To make the presentation concise and easy to understand, we will use an example to illustrate the design as shown in Figure 1.
An ESA must have the following behaviors. First, at the core is the Domain Events: “Something happened that the domain experts care about” [3]. Since a change to an Aggregate occurs only through its root, executing a root’s command generates and stores a Domain Event as a record of the state change. Second, through the Event Sourcing pattern, an Aggregate can restore its state to any point in time; this is done by applying the sequence of previously stored Domain Events up to that point.
Figure 1 displays a User Entity, which is an aggregate root and has the commands rename and changeEmail. The rename command of a User instance can proceed in three steps: (1) creating a UserRenamed event to represent the intended name change; (2) applying the UserRenamed event to change the name; and (3) if successfully renamed, storing the UserRenamed event in the User instance. The command changeEmail proceeds similarly, but with an event representing the specific intended change, e.g., EmailChanged. We utilize the Template Method pattern [30] to extract the abstract class EsAggregateRoot that provides a template method to facilitate a consistent execution of the three steps across all concrete aggregate roots derived from EsAggregateRoot (see the User class of Figure 1).
In Figure 1, domain events produced by a concrete aggregate root are instances of classes derived from the base type DomainEvent. In the example, UserRenamed and EmailChanged represent the two classes of events generated by a User object.
The abstract class EsAggregateRoot has three operations: apply, when, and addDomainEvent, all of which accept a DomainEvent object. Refer to note ➀ in the upper right corner of Figure 1. The template method apply defines the common processing steps enforced on the commands of concrete subclass User [30], including the abstract when operation, which is deferred to subclass User for processing the event, and the concrete addDomainEvent operation, which adds an event to the EsAggregateRoot and is reused by subclass User.
In Figure 1, the aggregate root User is a subclass of EsAggregateRoot. The User commands (the rename operation and the changeEmail operation in this example, as shown in note ➁ and note ➂) are executed by creating a DomainEvent object (specifically, a UserRenamed event and an EmailChanged event in the example) and calling the template method apply with it.
The User’s overridden when operation implements event-specific processing in the switch-case statement; please refer to note ➃ in the lower right corner of Figure 1.
In DDD, aggregates are persisted to a repository. Under Event Sourcing, when a User instance is persisted to the Repository [6] (pp. 147–161), Domain events stored in the instance are emptied out to the Repository. To reconstitute a User instance from the Repository, the system starts with a blank instance, retrieves its events, and applies them to bring the instance to its latest state. The reconstituted instance then begins another active episode with an empty event list. The design and code structure of Figure 1 serves as a foundation on which root contracting is built (Section 3).

2.3. Motivation of Root Contracting

Since 2018, while developing a Kanban system named ezKanban (see [31] for more details), we have incrementally adopted DDD, Clean Architecture [32], Behavior-Driven Development (BDD) [33], Event Sourcing, and Command Query Responsibility Segregation (CQRS) [34]. We have used ezKanban as an example exercise in our DDD, Clean Architecture, Event Sourcing, and CQRS courses, training more than seven hundred developers from various industries in Taiwan. The majority of these developers work in the banking sector, where there has been a significant demand for developing ESSs and microservices in recent years.
During the early stages of development, we relied heavily on use case tests and unit tests to ensure the correctness of ezKanban’s domain models. This means that if we had been invited by Zhong et al. to their empirical study [35], we would have belonged to the 9.8% of the respondents utilizing testing to ensure correctness between models and code.
However, as ezKanban evolved, we observed that the unit tests were growing in number and complexity to cover all classes in aggregates. Consequently, maintaining the unit tests was increasingly costly. The realization prompted us to explore other alternatives to unit testing as a means for ensuring the correctness of aggregates.
The opportunity revealed itself after we adopted Event Sourcing and transitioned to ESAs. With the code structure of ESAs and built on Chen et al.’s experience in developing a full-fledged Java DbC extension called ezContract [22], we recognized that we could apply a constrained form of DbC as the primary means to ensure aggregate correctness, providing a viable alternative to unit testing.

3. The Proposed Method of Root Contracting

Building on the design and code structure for implementing ESAs (Section 2.2), this section introduces root contracting, which applies DbC exclusively to the aggregate roots of ESAs to ensure correctness. First, Section 3.1 adapts the DbC correctness rules for ESAs in both normal and exceptional situations. Then, Section 3.2 presents how root contracting is applied and demonstrates that it enforces the adapted correctness rules.

3.1. Aggregate Root Correctness Rules

Eric Evans emphasized the importance of maintaining aggregate consistency as central to Domain Events (“… We keep the aggregates internally consistent at all times, …”) and Aggregates (“… Within an aggregate boundary, apply consistency rules synchronously. …”) [3]. Since an aggregate changes state only by executing a command on its root, its consistency can be ensured by requiring the aggregate root to obey the class correctness rules of DbC [5]. We begin with the following notation for describing the correctness rules.
Definition 1.
{ P } A { Q } : Whenever A is executed in a state satisfying P , the execution will terminate in a state satisfying Q , where A is an operation of an object, and P and Q are assertions on the object’s state that evaluate to true or false.
We customize the notations used in the correctness rules to fit the present DDD context. By command-query separation (CQS) [5], an aggregate root distinguishes its operations into commands that change the aggregate state and queries that provide information without changing the state. We define the aggregate root class correctness rules as defined below.
Definition 2.
Let m denote a command of an aggregate root class K. There are three types of commands: m = c , d, and f, respectively, for a  construction command, a  destruction command, and an  ordinary command. Let I N V denote the aggregate invariant of class K. Let p r e m , p o s t m , and b o d y m denote the precondition, the postcondition, and the body of command m. The  aggregate root class correctness rules  are defined by the four rules below:
{ p r e c } b o d y c { p o s t c a n d I N V }
{ p r e f a n d I N V } b o d y f { p o s t f a n d I N V }
{ p r e d a n d I N V } b o d y d { p o s t d }
{ t r u e } e x c e p t i o n a l e x i t o f b o d y m { I N V } , m = d , f
When a precondition p r e m , postcondition p o s t m , or aggregate invariant I N V evaluates to false, a corresponding contract violation exception is raised. These exceptions enable a program to handle contract violations in a disciplined way [5]. Between the client and the aggregate root’s command, a precondition violation exception informs the client that it has failed to meet its obligation as a caller. A postcondition violation exception alerts the client to bugs in the command’s implementation. Lastly, an invariant violation exception signals to the client that the aggregate root’s state is incorrect, and the client should not try to call the command again.
Note that implementing the correctness rules is a necessary but not a sufficient condition for assuring aggregate correctness. Ultimately, ensuring aggregate correctness through root contracting depends on the strength of its contract. To this end, the weakest strength is true, meaning there is no contract checking. Contract strength increases as more assertions are added, i.e., the precondition to assert the validity of command parameters and the postcondition to assert the expected state change, and the aggregate invariant to assert what must be true before and after executing a command.
Rules (1), (2), and (3) address normal situations, while rule (4) handles exceptional cases. They are elaborated in the next two subsections.

3.1.1. Correctness Rules for Normal Situations

Correctness rules (1) and (2) cover the exception-free situations; they are direct adaptations of DbC’s class correctness rules C1 and C2, respectively [5] (p. 370). Correctness rule (1) means that an instance of an aggregate root is created only when the construction command’s precondition is satisfied, and the instance created satisfies the postcondition and the aggregate invariant. Correctness rule (2) means that the body of an aggregate root’s ordinary command is executed only if the command’s precondition and the aggregate invariant are satisfied, and the execution of the body satisfies the command’s postcondition and the aggregate invariant.
Correctness rule (3) is added to deal with the deletion of an aggregate root in the context of implementing the pattern Domain Events. Since the destruction of an aggregate root publishes an event, a deleted aggregate root must still reside in memory for this to happen. Effectively, the deleted aggregate root is only marked as deleted but should not be used further. Therefore, correctness rule (3) does not check the aggregate invariant after body d . Note that checking an aggregate root for not being marked as deleted is an assertion in the aggregate invariant of every aggregate root; calling a command of a marked-as-deleted aggregate root throws an invariant violation exception (refer to the example code mentioned in Section 3.2.3, line 21).

3.1.2. Correctness Rules for Exceptional Situations

In addition to the three correctness rules for normal situations, we still need to further adapt Eiffel’s rules to address exceptional situations in root contracting.
In Eiffel, the correctness rules for the exceptional situations are C3, the correctness rule for failure-inducing rescue clauses, which restores the object of the failed command into a correct state without retrying (also known as organized panic) and C4, the correctness rule for retry-inducing rescue clauses, which attempts a retry after restoring the object to a correct state (also known as retry) [5] (p. 429).
Correctness rule (4) of Definition 2 adapts Eiffel’s correctness rule C3, which applies when a business processing exception is raised in the command body. In this case, an ordinary or a destruction command fails accordingly, but it restores the aggregate root’s invariant before throwing the exception. Note that a construction command does not restore the aggregate invariant because an aggregate root is not created, and nothing is to be done for correctness.
Eiffel’s rule C4 takes effect when the execution of the rescue clause reaches an instance of the reserved word retry. In our work, the responsibility for ensuring C4 is split between the failing command and its client: the failing command is responsible for ensuring correctness rule (4). In contrast, the client is responsible for retrying, which puts the correctness rule (2) in effect. Thus, no new rule is needed to adapt C4.
Past experience indicates that simulating retry mechanisms in languages without native retry support often leads to exception-handling anti-patterns, such as Nested Try Block and Spare Handler [36]. Because most mainstream languages do not provide built-in retry mechanisms [37], we recommend treating the command body implementation as a black box. Developers are free to implement retry logic in any suitable way, as long as they adhere to the correctness requirements specified in rule (2).

3.2. The Proposed Root Contracting

The abstractions of DomainEvent and EsAggregateRoot establish a design and code structure of Figure 1 that enables us to enforce the aggregate root correctness rules of Definition 2. Figure 2 illustrates this extension through code and comments highlighted in bold, where the notes are numbered in the same order as in Figure 1 for easy cross-reference.

3.2.1. Adding Precondition and Postcondition Checks in Command Bodies

The DbC enhancement to a command includes asserting its precondition before calling the template method apply, and asserting its postcondition after the template method apply returns without raising an exception; see the bold-faced lines in notes ➁ and ➂ of Figure 2a for the commands rename and changeEmail. If the precondition or postcondition is violated, the command terminates by raising a precondition violation exception or a postcondition violation exception, respectively.
It is worth noting that the ESA code structure of Figure 2a effectively makes a command a single-entry and single-exit routine. This feature simplifies the tool design for root contracting (see Section 4.3).

3.2.2. Adding Invariant Checks and Exception Handling in Template Method

The DbC enhancement to the template method apply includes checking the aggregate invariant through the hook operation ensureInvariant and handling the exception raised during event processing through the concrete operation ensureInvariant(RuntimeException); see the bold-faced methods in classes EsAggregateRoot and User, and note ➄ in Figure 2a.
The implementation of the template method apply is shown in Figure 2b (see also note ➀ of Figure 2a) as explained below:
  • Lines 2–3, the template method apply ensures the aggregate invariant by calling the hook operation ensureInvariant except when the event is a construction event, in which case the aggregate does not yet exist. A concrete aggregate root overrides the hook operation as shown in the User class; see note ➄ of Figure 2a.
  • Lines 4–10, the try-catch block delegates event processing to the primitive operation when for business processing and handles any exception raised there. In lines 7–8, an exception raised in processing a construction event is rethrown without asserting the aggregate invariant since the aggregate was not created. For any other event that raises an exception, the exception is delegated to the operation ensureInvariant(RuntimeException) for further processing (see the last bullet point below).
  • Lines 11–12, the template method apply checks for an aggregate root destruction event and skips asserting the aggregate invariant through ensureInvariant since the aggregate has been deleted. Otherwise, the aggregate invariant is asserted for other events.
  • Lines 15–23, the method ensureInvariant(RuntimeException) takes a business processing exception raised by the operation when as its parameter and rethrows it if the aggregate invariant asserts successfully; otherwise, it throws an invariant violation exception, making the raised exception its cause. Semantically, the client can attempt a retry in the former case since the aggregate invariant holds despite the raised exception; the client should not attempt a retry in the latter case since the aggregate invariant does not hold.
The implementation of Figure 2 satisfies the four adapted correctness rules of Definition 2. The implementation described in notes ➀, ➁, ➂, and ➄ of Figure 2a covers correctness rules (1), (2), and (3); the code in lines 4–10 and 15–23 of Figure 2b covers correctness rule (4).

3.2.3. An Example of rename Command in User Aggregate

Figure 3 continues on the example of User aggregate root and its rename command in Section 2.2 to illustrate root contracting with uContract. In implementing User, the developer only needs to add checking of precondition and postcondition to the command bodies of its methods, and override the hook method ensureInvariant. The developer reuses the template method apply for all the other DbC behaviors.
Table 1 summarizes the mapping from the theoretical concepts (i.e., the specific type of contract) to the design described in the notes of Figure 2a, the supporting uContract utility functions, and the corresponding code blocks in Figure 3.
The rename command’s precondition asserts the newName parameter to be non-null and non-empty with the utility function require (lines 8–9); the value oldDomainEventSize captures the old domain event size for asserting the domain event size in postcondition with the utility function old (line 11); the postcondition asserts that the changed user name is equal to the newName and the domain event size is increased with the utility function ensure (lines 15–16); and the aggregate invariant asserts that the User instance is not marked as deleted, its attribute “name” is non-null and non-empty, and attribute “id” is non-null with the utility function invariant (lines 21–24).

4. uContract: An Open-Source Java Utility for Root Contracting

To support root contracting, we have created uContract, an open-source Java utility and a demonstration project that illustrates its use, both available on GitLab [38,39]. This section focuses on the utility functions of Table 1 for contract specification (Section 4.1) and mechanisms for contract monitoring (Section 4.2). Section 4.3 explains why uContract does not support subcontracting. Section 4.4 describes other important implementation details of uContract, focusing on assertion evaluation rule [5] and performance optimizations.

4.1. Contract Specification

Figure 4 lists the code of Contract, the main abstraction of uContract.
The methods require, ensure, and invariant (lines 9–16, 17–24, and 25–32) emulate Eiffel’s corresponding reserved words for contract specification. Each method takes two parameters: a string annotating the condition being checked and a lambda expression of type BooleanSupplier for the assertion. An assertion failure in require, ensure, and invariant raises an instance of PreconditionViolationException, PostconditionViolationException, and ClassInvariantViolationException, respectively (lines 13–14, 21–22, and 29–30). The failing assertion’s accompanying annotation is copied to the exception to indicate the cause of the contract violation.
The old method (lines 33–40) is exclusively used in the postcondition to let an assertion access the previous value of a changed entity or value object. It accepts two parameters and performs deep copying through JSON serialization [40]. The first parameter is a lambda of the generic type Supplier<T>, which returns an object when its get method is called (line 37). The returned object reference is then used to create a deep copy by serializing it into a JSON byte array. The JSON byte array is subsequently deserialized using the second parameter, TypeReference<T>, to reconstruct a deep copy of the original object (line 38).
uContract also offers a set of helper methods that act as syntactic sugar to keep contract expressions concise. For example, helper methods requireNotNull, ensureNotNull, and invariantNotNull ensure an object is not null; methods ensureResult and ensureImmutableCollection check the return value of a query method; and the ensureAssignable method verifies in a command’s postcondition that all other entities and value objects of an aggregate root, which remain unchanged by the command, retain their original values. Finally, the ignore method prevents the generation of a redundant event when such an event has no effect.

4.2. Contract Monitoring

In Figure 4, uContract uses class attributes to control contract monitoring. The class attribute DBC (line 2) globally enables or disables the checking of the precondition, postcondition, and class invariant simultaneously. The attributes CHECK_PRE, CHECK_POST, and CHECK_INV (lines 3–5) provide independent control over the checking of the precondition, postcondition, and class invariant, respectively; see lines 10, 18, 26, and 34 for their usage. These class attributes are configured through environment variables.
Since the method old is used only in the postcondition, a deep copy is performed only if postcondition checking is enabled (line 34).

4.3. Subcontracting

Since uContract is intended to support only root contracting, not full DbC, we have an opportunity to keep its design simple. One decision that helped keep uContract simple was not to support subcontracting. The reasons are twofold: only aggregate roots are contracted, and DDD practices advise against using polymorphic aggregates through inheritance [28]. Consequently, we can apply polymorphism and inheritance when designing the contract-free objects inside aggregates (see Section 5.1.1 for an example). A main benefit of not supporting subcontracting is that uContract does not have to implement the subcontracting rules (Section 2.1), which can involve custom compilation, preprocessing, and post-processing in the existing DbC extensions. Moreover, by using only the common language features, uContract is a language-independent utility and can be easily ported to other programming languages. Table 2 summarizes the key differences between root contracting with uContract and existing DbC extensions.
It is worth noting that the ESA code structure of Figure 2 in Section 3.2 greatly simplifies the uContract design. The single-entry and single-exit commands make it unnecessary to inject contracts into multiple potential exit points. In existing full DbC extensions, contract injection requires additional processing and tooling during contract translation.

4.4. Other Important Implementation Details

uContract enforces DbC’s assertion evaluation rule [5] (pp. 402–403), which prevents infinite recursion during assertion checks. If an assertion invokes a method in the process of contract checking, that method must suspend its own contract checking. In Figure 4, the entered variable (line 6) is a ThreadLocal<Boolean> variable [41] to enforce the assertion evaluation rule: it is set just before contract evaluation (lines 12, 20, 28, and 36), and reset just after contract evaluation (lines 15, 23, 31, and 39). The contract checks of the method called within an assertion return immediately if entered is true (lines 11, 19, 27, and 35).
To improve performance when contract monitoring is turned off at runtime, the contract checking methods take a lambda expression as the assertion in the second parameter (lines 9, 17, 25, and 33). Lambda expressions are evaluated only when necessary (lines 13, 21, 29, and 37), i.e., contract checking is enabled and not called recursively (lines 10–11, 18–19, 26–27, and 34–35). In contrast, when a Boolean argument is used for the conditions, it will always be evaluated at the method call site.

5. Experiment: Performance Overhead Analysis

Running correctness checking programs (such as tests and contracts) takes time. Since contract checking can be enabled not only during software development but also in production, developers need to understand its overhead. To address this, this section conducts two experiments to analyze the additional execution overhead. The comparisons of other aspects are presented in Section 6.
The first experiment measures performance overhead for the four contract monitoring configurations recommended by DbC [23]: no contract checking (called all_off hereafter), precondition only (called pre_on—the default configuration recommended for production), precondition and postcondition (called pre_post_on), and precondition, postcondition, and class invariant (called all_on).
The second experiment compares the execution of the pre_on configuration by uContract against its non-DbC equivalent, where the precondition is written in standard Java code and utilities following defensive programming practices.

5.1. Experiment Setup

This subsection describes the experiment subject (Section 5.1.1) and the design of the two experiments (Section 5.1.2).

5.1.1. Experiment Subject: ezKanban’s Workflow Aggregate

We use the web application ezKanban [31], a multi-user application built with DDD and Event Sourcing to support the Kanban method [42], as the development case. The Workflow aggregate is the subject against which contract execution overhead is measured—see Figure 5 for its description, where italicized words such as board, workflow, root stage, etc., are terms from ezKanban’s ubiquitous language [6].
Figure 6 illustrates the domain model of the Workflow aggregate. The Workflow entity is the aggregate root and has twelve commands; it is the contracting target. All told, Workflow totals 382 assertions. Within the aggregate, the abstract Lane class generalizes the Stage and SwimLane entities. The WIPLimit value object specifies the work-in-progress limit of a lane. These internal objects are not contracted.

5.1.2. Experiment Design

Table 3 lists the uContract utility functions called from the precondition, postcondition, and class invariant across the four monitoring configurations. The execution overhead of the relevant functions is recorded as indicated by a check mark “v”.
To exercise the contracts, we use assertion-free JUnit tests to invoke the commands following the AAA pattern [43]: set up the workflow and its stages (Arrange), call the command (Act), and skip the assertions (Assert) because the contracts already cover them.
The measurements of the experiments are collected on a computer with the specifications in Table 4. Execution time is measured with Java’s System.nanoTime(). Java Flight Recorder (JFR) [44] is used to collect CPU execution samples. The sampling period for jdk.ExecutionSample events is configured to 1 millisecond. The measurements are averages over 200 runs. To reflect the impact of aggregate size (i.e., of the number of objects in an aggregate) on the execution, we further differentiate an experiment into three sub-experiments with one single workflow containing 10 stages (code-named Small), 100 stages (code-named Medium), and 500 stages (code-named Large).

5.2. Results

We now describe the two results in detail and make observations on the measurements taken at the two experiments. For more details of the experiment, we refer the readers to the GitLab repository [45].

5.2.1. Experiment 1: Performance Overhead of Root Contracting with uContract

The first experiment measures the execution overhead to execute each of Workflow’s twelve commands once. Table 5 presents the average execution times, from which the following observations are made:
(1)
The execution of the all_off configuration spends 0.407–0.495 ms. The execution times are attributed to checking the flags CHECK_PRE, CHECK_POST, and CHECK_INV and handling the lambda functions in the assertions (see Section 5.3).
(2)
The execution times of the default pre_on configuration are 0.677–0.768 ms, an increase of about 0.2–0.3 ms compared to their counterparts in the all_off configuration. As the precondition mainly checks the input value to be non-null or non-empty, the execution times only increase slightly.
(3)
The execution times of the pre_post_on configuration fall in the range of 19.916–546.059 ms, a significant increase compared to their counterparts in the default pre_on configuration. The increases reflect mainly the time spent to check that the stages untouched by a command remain unchanged, as will be explored below in Table 6.
(4)
The execution times of the all_on configuration are 20.592–550.035 ms, a relatively small increase of about 0.6–5.8 ms from the pre_post_on configuration due to further checking the class invariant. In this experiment setup, the overhead is much smaller compared to the overhead of checking the postcondition.
To further explore observation (3) above, we present the measurements of the uContract utility functions in the all_on configuration, in the order of execution time (Table 6) and the proportion of CPU time consumed by contract (Table 7).
In Table 6, it can be observed that the execution times increase as the number of stages increases. In particular, the ensureAssignable and ensure functions account for most of the increased time to check that the stages not modified by the commands remain the same. Also, the time spent by the old function is linearly proportional to the number of stages due to deep-copying.
In Table 7, the proportion of CPU time consumed by contract checking R c is defined as follows:
R c = t c t c + t d l ,
where t c and t d l are the CPU times consumed by checking contracts and executing domain logic by the twelve commands, respectively.
Since the twelve commands have simple domain logic, almost all of the CPU time is spent on checking contracts, as shown in the last row of Table 7. Note that postcondition checking accounts for the largest share of CPU time consumed. Further, observe the shifts of percentages spent by the functions old, ensureAssignable, and ensure as the number of stages increases.

5.2.2. Experiment 2: Precondition Performance Overhead of Standard Java vs. uContract

The precondition checks whether the client makes a mistake calling a method of the supplier. Thus, they must be checked even if DbC is not used. This experiment compares the execution times consumed by precondition checking when (1) writing the precondition with ObjectUtils, java.util, and ordinary Java code, and (2) writing the precondition with uContract.
The results of execution times are shown in Table 8. The execution times of (2) are 0.677–0.768 ms, an increase of about 0.5 ms compared to their counterparts (1), which are 0.161–0.313 ms. The increased time is due to checking the flags CHECK_PRE, CHECK_POST, and CHECK_INV for contract monitoring and handling the lambda functions in assertions of the precondition (see Section 5.3).

5.3. Discussion of Experimental Findings

The extra execution time of adopting uContract is because each utility function executing the BooleanSupplier has an extra compilation effort for the first execution at runtime. Java lambda expressions, introduced in Java 8, are compiled using the invokedynamic instruction. When Java Virtual Machine (JVM) first encounters an invokedynamic instruction, it calls the bootstrap method to link the invokedynamic instruction and a method handle, and returns a CallSite object representing the linked state. When JVM re-encounters the same invokedynamic instruction later, it automatically calls the linked method handle without calling the bootstrap method [46]. That is, the extra time is spent while the utility function executes the BooleanSupplier for the first time in uContract, but optimizes for any other times.

6. Comparative Evaluation

This section compares uContract with other DbC tools in various aspects. Building on the previous work of Chen et al. [18,22], we compare these tools using a taxonomy of contract specifications in Section 6.1. We further analyze the tools with four quality attributes: sustainability, usability, portability, and developability in Section 6.2. Some other alternative forms of comparison are reviewed in Section 6.3.

6.1. A Taxonomy of Contract Specification

Chen et al. surveyed DbC tools for Java and proposed a classification scheme based on contract specification [18,22]. Building on this foundation, this paper broadens the scope by including DbC tools implemented in multiple programming languages that have been actively maintained over the past five years, as shown in Figure 7.
The DbC tools fall into two categories: contracts specified in the Host language and those in the Purpose-built language. Two new subcategories are added under the Host language category: Utility and Contracts as expressions in annotations; refer to the top two branches in Figure 7. Unlike DbC extensions, these subcategories leverage host language features to implement DbC features without requiring language extensions or additional processing tools. uContract is found under the Utility subcategory.
In the Utility subcategory, contracts are specified via utility functions or classes using standard features of the host language, including uContract in Java [38], godbc in Go [47], and Boost.Contract in C++ [48]. These libraries provide contract-related functions that developers explicitly invoke within a program.
In the Contracts as expressions in annotations subcategory, contracts are specified via expressions of the host language. For example, icontract [49] offers the @require, @ensure, and @invariant decorators that carry an assertion as a Python expression, followed by an optional description. This is done similarly in the Rust DbC tool contracts [50] with the attributes #[require], #[ensure], and #[invariant].
In addition to the two new subcategories, there are three other subcategories in the Host language category: Keyword extension, Naming convention, and Marker. In the Keyword extension subcategory, contracts are specified via the extended keyword that requires a custom compiler. In the Naming convention subcategory, contracts for a method with name m are written as protected methods adorned with the type of contracts, for example, m_PreCondition for the precondition of method m in jContractor. In the Marker subcategory, contracts are specified via assertions in a code region delimited by a pair of markers, e.g., Require.begin() and Require.end() in ezContract.
In the Purpose-built language category, there are three subcategories: Comment, Contracts as strings in annotations (Contracts in Java annotation [18]), and Aspect-like constructs. In the Comment subcategory, contracts are specified via comments, requiring a preprocessor to convert them into the host language. In the Contracts as strings in annotations subcategory, contracts are specified as string literals in annotations, adhering to contract language syntax for processing before or after compilation. In the Aspect-like constructs subcategory, contracts are defined separately like aspects in Aspect-Oriented Programming (AOP), which are woven into specific targets (e.g., a class or interface) via a contract compiler.

6.2. A Quality-Attributes-Based Comparison

To compare uContract with other tools, we surveyed DbC tools that have been actively maintained over the past five years based on four quality attributes: sustainability, usability, portability, and developability. Table 9 summarizes the comparisons. The comparison is qualitative in nature, and each tool is rated high, medium, and low per attribute according to our analysis.

6.2.1. Sustainability

Sustainability refers to whether a tool is likely to receive long-term maintenance as the host language evolves. The sustainability of a DbC tool is strongly associated with the processing phases—preprocessing, compilation, and post-processing—of contract translation, which involves transforming high-level contract specifications into a form that can be used for runtime checking. Table 10 lists the processing phase(s) of contract translation for the selected DbC tools of Figure 7.
The sustainability of a DbC tool depends on the processing phases in two ways. First, the more phases that require processing, the less sustainable the DbC tool becomes. Second, within a phase, a DbC tool that relies on a custom tool (for example, the custom Java compiler in JML and the custom preprocessor in dbc4go) is less sustainable than one that uses a feature available in the host language (such as decorators in icontract and deal for Python). Refer to the second column of Table 9 for the tools’ ratings in sustainability. A tool is rated high if it uses host language features in a single processing phase, medium if it uses host language features across multiple processing phases, and low if it uses any custom tool.

6.2.2. Usability

Usability refers to how frictionless a DbC tool can be used. It should be easy to learn, efficient to use, and free of frustration. Following Chen et al. [18], this paper uses the attributes below to evaluate usability.
  • Source compatibility (SC) indicates whether the extended DbC tool can be used without modifying the host language, IDE, or existing pluggable features.
  • No symbolic barrier (NSB) determines whether a contract and its target are related from the compiler’s point of view. A symbolic barrier exists if a change in the contract does not cause the compiler to generate an error on its target, or vice versa.
  • Line number information for debugging (LNIFD) specifies whether a DbC tool shows the line number of the assertion statement that raises a contract violation exception.
  • Syntax-aware specification (SAS) determines whether a compiler or an interpreter checks syntax errors in contracts.
  • Collocation of contract and target (COCAT) indicates whether contracts collocate with the contracted target.
  • Low decommissioning impact (LDI) defines the level of impact on the contracted program caused by decommissioning contracts.
Table 11 lists the attributes of each tool. The more attributes a tool has, the more usable it is. It is worth noting that all tools have the COCAT attribute, suggesting that recent tools generally support the idea that contracts should collocate with their targets. Earlier tools, such as HandShake and jContractor, do not have the attribute. Refer to the third column of Table 9 for the tools’ ratings in usability. A tool is rated high if it has more than three usability attributes, medium if it has exactly three, and low if it has fewer than three.

6.2.3. Portability

Portability refers to the extent to which a DbC tool can be ported for use across different programming languages. DbC tools that use language features available in most languages will be the most portable. DbC tools that require a custom compiler, interpreter, preprocessor, and postprocessor will require more porting effort. Finally, tools that rely on language-specific features unavailable in most languages, such as Python’s decorators and Rust’s procedural macros, tend to have a more limited portability. Refer to the fourth column of Table 9 for the portability ratings. A tool is rated high if it only uses standard language features, medium if it uses a custom compiler, interpreter, preprocessor, or postprocessor, and low if it uses language-specific features.

6.2.4. Developability

Developability refers to the degree of difficulty involved in implementing and maintaining a DbC tool. DbC tools that rely on common language features, such as if statements, assertions, and lambdas, tend to offer the highest developability. Tools that depend on language-specific features, such as Python’s decorators and Rust’s procedural macros, generally require more development effort. Finally, tools that require a custom compiler, interpreter, preprocessor, or postprocessor will demand the greatest development effort. Refer to the fifth column of Table 9 for the developability ratings. A tool is rated high if it uses common language features, medium if it uses language-specific features, and low if it employs any custom preprocessor, interpreter, compiler, or postprocessor.
It should be noted that uContract does not support subcontracting. When using uContract for comprehensive DbC, developers will need to derive the contracts of subclasses. In this case, its developability score drops to medium.

6.2.5. Overall Ratings

To assist developers in choosing an appropriate DbC tool, each tool is given an overall rating based on four attributes. Ratings of high, medium, and low correspond to 3, 2, and 1 points, respectively. The maximum possible score is 12 points, with higher scores indicating greater suitability. Refer to the sixth column of Table 9 for the overall ratings.
The ratings in Table 9 aim to reflect each tool’s potential to promote DbC adoption across different programming languages. They do not indicate the overall quality of the tools. According to the ratings, uContract and godbc have the highest potential (12 points), followed by icontract, deal, and decorator-contracts (9 points), with Boost.Contract and contracts scored 8 points.

6.3. Other Relevant Aspects

Other comparison methods exist. For example, Aghaei evaluated nine Java DbC tools based on the following criteria: (1) whether the tool supports Eclipse, (2) whether the tool provides a plugin, (3) whether the tool supports custom constraints, (4) whether the tool depends on an external library, (5) whether the tool provides full documentation and a user guide, and (6) ease of use [51].
Aghaei also compared the compile time and memory consumption of DbC tools, but only five out of nine tools could be measured [51]. While this comparison provides valuable insights for evaluating DbC tools in real-world projects, conducting a fair assessment of performance and memory usage remains challenging. Some tools are no longer maintained, and others may not be compatible with the same Java versions, making direct comparisons unreliable, if not infeasible.
Besides the constraints discussed in Section 4.3, uContract performs runtime assertion checking and does not provide compile-time contract verification capability, whereas Eiffel (with AutoProof [52]), OpenJML [15,53], and Spec# [20]) use Satisfiability Modulo Theories (SMT) solvers for compile-time contract verification.
Another aspect of feature comparison is the additional features provided by DbC tools. For example, JML not only supports DbC but also facilitates test case generation and documentation generation from contracts [15]. However, these additional functionalities are beyond the scope of our study and are therefore not discussed further.

7. Discussion

Since root contracting is a constrained form of DbC, it is important to understand its impact and the trade-offs involved. This section analyzes the relationships among proposed root contracting, comprehensive DbC (per Eiffel), and DbC using an extension (for other languages like Java and C#) from the perspectives of (1) correctness and robustness, and (2) applicability.

7.1. Correctness and Robustness

First, it is important to understand that comprehensive DbC cannot be implemented outside the Eiffel environment, even when using a DbC extension. While DbC extensions are available in languages such as Java or C#, comprehensive and seamless integration of DbC remains a distinctive feature of the Eiffel environment. Not only does Eiffel have reserved words for contract specification, but its standard libraries are also fully contracted. This allows a caller to distinguish between contract exceptions and routine failure exceptions, enhancing robustness through disciplined exception handling [5]. If an exception is thrown due to violating a precondition, a postcondition, or a class invariant, the caller or the external application programming interface (API) is responsible, respectively. When a non-contract exception is thrown, the caller understands the cause is elsewhere, and neither it nor the API is responsible.
Outside Eiffel, although developers can specify contracts by using a DbC extension, the contracts end with the objects they create. Whenever a call is made to an external API—including APIs from the standard library of the host language or any third-party library—the comprehensiveness of DbC is compromised because these APIs are not contracted. Whenever an API raises an exception, the caller only knows an error has occurred, but does not have the information needed for disciplined exception handling.
Root contracting aims to strike a balance between the comprehensive DbC of Eiffel and the lack of disciplined exception handling seen in DbC extensions. In the template method apply of Figure 2 of Section 3, root contracting either throws an invariant violation exception attributing the exception raised by an API as its cause, or re-throws it. The design allows the aggregate root’s client to handle the exception correctly. Specifically, the client can retry the failed aggregate root command when it catches a non-contract exception because it knows the aggregate invariant holds; the client should not attempt to retry the failed command when it sees an invariant violation exception.

7.2. Applicability of Root Contracting in Broader Contexts

Although root contracting may seem like a niche method due to its applicability in the specific context of ESAs, it represents a common approach in assuring the correctness of ESSs and microservice architecture systems, which are increasingly adopted in modern software dealing with complex business logic [2,35].
Although root contracting applies to DDD domain models, it benefits from the application of a layered architecture. In the case of Clean Architecture, the use cases layer depends unilaterally on the entities layer, with the constraint that each use case invokes the commands of the same aggregate root. Root contracting benefits from this relationship when use case tests are written, because the use case tests can readily exercise the contracts when they call the commands, replacing the assertion-free unit tests as the contract exercisers. This has the positive consequence of reducing the size of tests.
The benefit is further increased by integrating root contracting with BDD, where developers write use case tests as Given–When–Then Gherkin scenarios to drive the specification and execution of contracts. This approach provides a viable alternative to Contract-Driven Development (CDD), which begins with specifying the GUI and relies on tools to generate test cases for exercising contracts [54].
It should be noted that adopting the ESA code structure, as shown in Figure 1, does not necessarily require developers to use Event Sourcing as a persistence mechanism. In DDD, the Repository pattern determines the choice of persistence mechanism [6] (p. 152). Developers can implement ESAs while utilizing a Repository implementation that stores the current state in a relational database. In other words, the ESA code structure provides the flexibility to defer the persistence decision until deployment time. Even if developers do not use Event Sourcing, structuring aggregates as ESAs remains a viable approach to enabling root contracting.

8. Conclusions

We proposed a novel adaptation of Design by Contract called root contracting to ensure the correctness of aggregates in the context of building systems with Event Sourcing and Domain-Driven Design. The proposed method contracts the roots of aggregates. We adapt the class correctness rules of Design by Contract, a foundation for achieving correctness and robustness in object-oriented software, for event-sourced aggregates. We have demonstrated that the design and code structure of event-sourced aggregates facilitate root contracting as a lightweight approach, using a utility like uContract, rather than relying on Design by Contract extensions. This approach avoids the known issues of the latter, which have previously hindered the sustainable adoption of Design by Contract, as discussed in this paper. With the single exit point design and without the subcontracting support, the implementation of uContract is relatively straightforward and can be ported across most programming languages, which avoids developers being tied down by uContract. Additionally, the experiment results indicate that uContract’s overhead primarily stems from verifying the postcondition, which is enabled during development and testing. In production, typically only the precondition is enabled, and its performance overhead is comparable to that of writing the precondition with ordinary Java code.
While root contracting is applicable to ensure the correctness of Event-Sourced Systems developed with Domain-Driven Design, its usefulness is generally limited in object-oriented systems due to the dependency on the code structure of event-sourced aggregates and the lack of subcontracting support.
Beyond the performance evaluation conducted in this study, we plan further to validate the robustness of root contracting through mutation testing and compare its effectiveness with that of unit testing, among other evaluation methods. Moreover, the mutation score can serve as a quantitative indicator of verification strength, helping to address the question of how much contracting is sufficient.
Since Design by Contract can be seamlessly adopted across languages, root contracting embodies the principle of “doing the thing right.” In the era of Large Language Model (LLM)-assisted coding, we envision root contracting not only as a mechanism to constrain event-sourced aggregates but also as a broader means to mitigate hallucinations and enhance reliability in AI-assisted software development.

Author Contributions

Conceptualization, C.-T.C. and Y.C.C.; data curation, Y.-C.Y.; funding acquisition, Y.C.C.; investigation, C.-T.C., Y.-C.Y., and Y.C.C.; methodology, C.-T.C., Y.-C.Y., Y.-H.H., and Y.C.C.; project administration, C.-T.C. and Y.C.C.; resources, C.-T.C. and Y.C.C.; software, C.-T.C., Y.-C.Y., and Y.-H.H.; supervision, C.-T.C. and Y.C.C.; validation, C.-T.C., Y.-C.Y., and Y.C.C.; visualization, Y.-C.Y. and Y.C.C.; writing—original draft, C.-T.C., Y.-C.Y., Y.-H.H., and Y.C.C.; writing—review and editing, C.-T.C., Y.-C.Y., Y.-H.H., and Y.C.C. All authors have read and agreed to the published version of the manuscript.

Funding

This research was supported by the National Science and Technology Council, Taiwan grant numbers NSTC 111-2221-E-027-079 and NSTC 112-2221-E-027-050-MY3.

Institutional Review Board Statement

Not applicable.

Informed Consent Statement

Not applicable.

Data Availability Statement

The authors confirm that the data supporting the findings of this study are available within the article and can be reproduced from the GitLab repository: https://gitlab.com/TeddyChen/ucontract_performance_experiment; https://gitlab.com/TeddyChen/root-contracting; and https://gitlab.com/TeddyChen/ucontract (accessed on 23 September 2025).

Acknowledgments

We thank the ezKanban team members Mandy Yang, Kevin Hsu, Richard Cheng, Alan Cai, Eddie Chou, and Anita Cheng for their contribution to the development of ezKanban.

Conflicts of Interest

Author Chien-Tsun Chen was employed by the company Teddysoft. The remaining authors declare that the research was conducted in the absence of any commercial or financial relationships that could be construed as a potential conflict of interest.

References

  1. Emily, H.; Oliver, B. Event-driven architectures in modern systems: Designing scalable, resilient, and real-time solutions. Int. J. Trend Sci. Res. Dev. 2020, 4, 1958–1976. [Google Scholar]
  2. Overeem, M.; Spoor, M.; Jansen, S.; Brinkkemper, S. An Empirical Characterization of Event Sourced Systems and Their Schema Evolution—Lessons from Industry. J. Syst. Softw. 2021, 178, 110970. [Google Scholar] [CrossRef]
  3. Evans, E. Domain-Driven Design Reference: Definitions and Pattern Summaries; Dog Ear Publishing: Indianapolis, IN, USA, 2014. [Google Scholar]
  4. Myllynen, T.; Kamau, E.; Mustapha, S.D.; Babatunde, G.O.; Alabi, A.A. Developing a Conceptual Model for Cross-Domain Microservices Using Event-Driven and Domain-Driven Design. Int. J. Multidiscip. Res. Growth Eval. 2023, 4, 635–638. [Google Scholar] [CrossRef]
  5. Meyer, B. Object-Oriented Software Construction, 2nd ed.; Prentice Hall: Englewood Cliffs, NJ, USA, 1997. [Google Scholar]
  6. Evans, E. Domain-Driven Design: Tackling Complexity in the Heart of Software; Addison-Wesley Professional: Boston, MA, USA, 2004. [Google Scholar]
  7. Wirfs-brock, R.J. Driven to ... Discovering Your Design Values. IEEE Softw. 2007, 24, 9–11. [Google Scholar] [CrossRef]
  8. Larman, C. Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development, 3rd ed.; Pearson: Upper Saddle River, NJ, USA, 2004. [Google Scholar]
  9. Kramer, R. iContract – the Java Design by Contract Tool. In Proceedings of the Technology of Object-Oriented Languages, TOOLS 26 (Cat. No. 98EX176), Santa Barbara, CA, USA, 3–7 August 1998; pp. 295–307. [Google Scholar] [CrossRef]
  10. Duncan, A.; Hölzle, U. Adding Contracts to Java with Handshake; Technical Report TRCS98-32; The University of California at Santa Barbara: Santa Barbara, CA, USA, 1998. [Google Scholar]
  11. Bartetzko, D.; Fischer, C.; Möller, M.; Wehrheim, H. Jass—Java with Assertions. Electron. Notes Theor. Comput. Sci. 2001, 55, 103–117. [Google Scholar] [CrossRef]
  12. Karaorman, M.; Abercrombie, P. jContractor: Introducing Design-by-Contract to Java Using Reflective Bytecode Instrumentation. Form. Methods Syst. Des. 2005, 27, 275–312. [Google Scholar] [CrossRef]
  13. Lackner, M.; Krall, A.; Puntigam, F. Supporting Design by Contract in Java. J. Object Technol. 2002, 1, 57–76. [Google Scholar] [CrossRef]
  14. Burdy, L.; Cheon, Y.; Cok, D.R.; Ernst, M.D.; Kiniry, J.R.; Leavens, G.T.; Leino, K.R.M.; Poll, E. An overview of JML tools and applications. Int. J. Softw. Tools Technol. Transf. 2005, 7, 212–232. [Google Scholar] [CrossRef]
  15. Leavens, G.T.; Cheon, Y. Design by Contract with JML. Available online: http://www.jmlspecs.org/jmldbc.pdf (accessed on 19 September 2025).
  16. Leavens, G.T.; Baker, A.L.; Ruby, C. Preliminary Design of JML: A Behavioral Interface Specification Language for Java. ACM SIGSOFT Softw. Eng. Notes 2006, 31, 1–38. [Google Scholar] [CrossRef]
  17. Feldman, Y.A.; Barzilay, O.; Tyszberowicz, S. Jose: Aspects for Design by Contract. In Proceedings of the Fourth IEEE International Conference on Software Engineering and Formal Methods (SEFM’06), Pune, India, 11–15 September 2006; pp. 80–89. [Google Scholar] [CrossRef]
  18. Chen, C.T.; Cheng, Y.C.; Hsieh, C.Y. Contract Specification in Java: Classification, Characterization, and a New Marker Method. IEICE Trans. Inf. Syst. 2008, E91.D, 2685–2692. [Google Scholar] [CrossRef]
  19. Lê, N.M. Cofoja. Available online: https://github.com/nhatminhle/cofoja (accessed on 19 September 2025).
  20. Barnett, M.; Leino, K.R.M.; Schulte, W. The Spec# Programming System: An Overview. In Proceedings of the Construction and Analysis of Safe, Secure, and Interoperable Smart Devices, Marseille, France, 10–14 March 2004; Barthe, G., Burdy, L., Huisman, M., Lanet, J.L., Muntean, T., Eds.; Springer: Berlin/Heidelberg, Germany, 2005; pp. 49–69. [Google Scholar]
  21. Chavacava. dbc4go. Available online: https://github.com/chavacava/dbc4go (accessed on 19 September 2025).
  22. Cheng, Y.C.; Chen, C.T.; Hsieh, C.Y. ezcontract: Using marker library and bytecode instrumentation to support design by contract in java. In Proceedings of the 14th Asia-Pacific Software Engineering Conference (APSEC’07), Nagoya, Japan, 4–7 December 2007; pp. 502–509. [Google Scholar] [CrossRef]
  23. Meyer, B. Applying ‘Design by Contract’. Computer 1992, 25, 40–51. [Google Scholar] [CrossRef]
  24. OpenJML. OpenJML. Available online: https://github.com/OpenJML/OpenJML (accessed on 19 September 2025).
  25. Microsoft. Code Contracts. Available online: https://learn.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts (accessed on 19 September 2025).
  26. Zimarev, A. Hands-On Domain-Driven Design with .NET Core: Tackling Complexity in the Heart of Software by Putting DDD Principles into Practice; Packt Publishing Ltd.: Birmingham, UK, 2019. [Google Scholar]
  27. Millett, S.; Tune, N. Patterns, Principles, and Practices of Domain-Driven Design; Wrox Press: Indianapolis, IN, USA, 2015. [Google Scholar]
  28. Vernon, V. Implementing Domain-Driven Design; Addison-Wesley Professional: Boston, MA, USA, 2013. [Google Scholar]
  29. Khononov, V. Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy; O’Reilly Media, Inc.: Sebastopol, CA, USA, 2021. [Google Scholar]
  30. Gamma, E.; Helm, R.; Johnson, R.; Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software; Addison-Wesley Professional: Boston, MA, USA, 1995. [Google Scholar]
  31. Hu, Y.H.; Chen, C.T.; Cheng, Y.C. How We Develop an Online Kanban Board Game in Two Months? In Proceedings of the 2022 International Conference on Computational Science and Computational Intelligence (CSCI), Las Vegas, NV, USA, 14–16 December 2022; pp. 1901–1906. [Google Scholar] [CrossRef]
  32. Martin, R.C.; Grenning, J.; Brown, S. Clean Architecture: A Craftsman’s Guide to Software Structure and Design; Prentice Hall: Englewood Cliffs, NJ, USA, 2018. [Google Scholar]
  33. Smart, J.F.; Molak, J. BDD in Action: Behavior-Driven Development for the Whole Software Lifecycle, 2nd ed.; Manning: Greenwich, CT, USA, 2023. [Google Scholar]
  34. Kumar, A. CQRS (Command Query Responsibility Segregation); Independently published, 2019. Available online: https://www.amazon.com/-/zh_TW/CQRS-Command-Query-Responsibility-Segregation/dp/1795874775#detailBullets_feature_div (accessed on 19 September 2025).
  35. Zhong, C.; Li, S.; Huang, H.; Liu, X.; Chen, Z.; Zhang, Y.; Zhang, H. Domain-driven design for microservices: An evidence-based investigation. IEEE Trans. Softw. Eng. 2024, 50, 1425–1449. [Google Scholar] [CrossRef]
  36. Chen, C.T.; Cheng, Y.C.; Hsieh, C.Y.; Wu, I.L. Exception handling refactorings: Directed by goals and driven by bug fixing. J. Syst. Softw. 2009, 82, 333–345. [Google Scholar] [CrossRef]
  37. Garcia, A.F.; Rubira, C.M.; Romanovsky, A.; Xu, J. A comparative study of exception handling mechanisms for building dependable object-oriented software. J. Syst. Softw. 2001, 59, 197–222. [Google Scholar] [CrossRef]
  38. Chen, C.T.; ezKanban team of Software System Lab of Taipei Tech. uContract v2.0.0. Available online: https://gitlab.com/TeddyChen/ucontract (accessed on 19 September 2025).
  39. Chen, C.T.; ezKanban team of Software System Lab of Taipei Tech. Root-Contracting. Available online: https://gitlab.com/TeddyChen/root-contracting (accessed on 19 September 2025).
  40. Ecma International. Standard ECMA-404: The JSON Data Interchange Syntax. Available online: https://www.ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf (accessed on 19 September 2025).
  41. Oracle and/or Its Affiliates. Class ThreadLocal<T>. Available online: https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html (accessed on 19 September 2025).
  42. Anderson, D.J. Kanban: Successful Evolutionary Change for Your Technology Business; Blue Hole Press: Milford, CT, USA, 2010. [Google Scholar]
  43. Khorikov, V. Unit Testing Principles, Practices, and Patterns; Manning Publications: Greenwich, CT, USA, 2020. [Google Scholar]
  44. Oracle and/or Its Affiliates. About Java Flight Recorder. Available online: https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/about.htm#JFRUH170 (accessed on 15 October 2025).
  45. Chen, C.T.; Yen, Y.C.; Hu, Y.H.; Cheng, Y.C. uContract_performance_experiment. Available online: https://gitlab.com/TeddyChen/ucontract_performance_experiment (accessed on 24 October 2025).
  46. Oracle and/or Its Affiliates. The Invokedynamic Instruction. Available online: https://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html#invokedynamic (accessed on 19 September 2025).
  47. Pabón, L. godbc v1.1.0. Available online: https://github.com/lpabon/godbc (accessed on 19 September 2025).
  48. Boost.org. boost-1.88.0. Available online: https://github.com/boostorg/contract (accessed on 19 September 2025).
  49. Parquery. icontract 2.7.1. Available online: https://github.com/Parquery/icontract (accessed on 19 September 2025).
  50. Ede, R. contracts v0.6.6. Available online: https://crates.io/crates/contracts (accessed on 19 September 2025).
  51. Aghaei, M. An Experimental Evaluation of Java Design-by-Contract Extensions. Master’s Thesis, University of Oulu, Oulu, Finland, 2018. [Google Scholar]
  52. Tschannen, J.; Furia, C.A.; Nordio, M.; Polikarpova, N. AutoProof: Auto-active Functional Verification of Object-oriented Programs. arXiv 2015, arXiv:1501.03063. [Google Scholar] [CrossRef]
  53. OpenJML. OpenJML—Formal Methods Tool for Java and the Java Modeling Language (JML). Available online: https://www.openjml.org/documentation/introduction.html (accessed on 12 October 2025).
  54. Leitner, A.; Ciupa, I.; Oriol, M.; Meyer, B.; Fiva, A. Contract Driven Development = Test Driven Development—Writing Test Cases. In Proceedings of the 6th Joint Meeting of the European Software Engineering Conference and the ACM SIGSOFT Symposium on The Foundations of Software Engineering, Dubrovnik, Croatia, 3–7 September 2007; pp. 425–434. [Google Scholar] [CrossRef]
Figure 1. Applying Template Method to handle Domain Events.
Figure 1. Applying Template Method to handle Domain Events.
Electronics 14 04205 g001
Figure 2. Root contracting: the proposed design and code structure.
Figure 2. Root contracting: the proposed design and code structure.
Electronics 14 04205 g002
Figure 3. Incorporating the precondition and the postcondition for the rename command and adding the aggregate invariant for User.
Figure 3. Incorporating the precondition and the postcondition for the rename command and adding the aggregate invariant for User.
Electronics 14 04205 g003
Figure 4. Contract class: the main abstraction of uContract.
Figure 4. Contract class: the main abstraction of uContract.
Electronics 14 04205 g004
Figure 5. A partial description of a Kanban workflow.
Figure 5. A partial description of a Kanban workflow.
Electronics 14 04205 g005
Figure 6. Class diagram of the Workflow aggregate.
Figure 6. Class diagram of the Workflow aggregate.
Electronics 14 04205 g006
Figure 7. A classification schema of contract specification methods.
Figure 7. A classification schema of contract specification methods.
Electronics 14 04205 g007
Table 1. Mapping from contract types to the design in Figure 2a, the supporting uContract functions, and code in Figure 3.
Table 1. Mapping from contract types to the design in Figure 2a, the supporting uContract functions, and code in Figure 3.
Contract TypeNote(s) in Figure 2auContract Function(s)Lines in Figure 3
precondition➁ and ➂requirelines 8–9
postcondition➁ and ➂old and ensureline 11 and
lines 15–16
aggregate invariantinvariantlines 21–24
Table 2. Key differences between root contracting and existing DbC extensions.
Table 2. Key differences between root contracting and existing DbC extensions.
FeatureExisting DbC ExtenstionsRoot Contracting with uContract
Contract Specification
Targets
Classes and interfacesEvent-sourced aggregate roots
DbC SupportFull supportNo subcontracting
Contract TranslationFull language compilers,
preprocessors, and
postprocessors
No translation needed
Table 3. The uContract utility functions that incur contract-checking overhead under the four configurations.
Table 3. The uContract utility functions that incur contract-checking overhead under the four configurations.
Contract TypeName of Operation/
Utility Function
Configuration
all_offpre_onpre_post_onall_on
Preconditionrequire or ignore vvv
Postconditionold vv
ensureAssignable vv
ensure vv
Class Invariantinvariant v
Table 4. Specifications of the computer used in the experiments.
Table 4. Specifications of the computer used in the experiments.
Computer Specifications
ProcessorIntel(R) Core(TM) i7-10700 CPU @ 2.90 GHz
Memory16.0 GB DDR4-2933 MHz
Storage1 TB HDD ST1000DM010-2EP102,
256 GB SSD HFM256GDJTNG-8310A
System Type64-bit operating system, x64-based processor
Operating SystemWindows 10 Pro [Version 10.0.19045.5854]
Table 5. Total contract evaluation times for the small, medium, and large profiles against the four monitoring configurations averaged over 200 runs.
Table 5. Total contract evaluation times for the small, medium, and large profiles against the four monitoring configurations averaged over 200 runs.
ConfigurationAverage Execution Time (ms)
SmallMediumLarge
all_off0.4950.4450.407
pre_on0.6960.6770.768
pre_post_on19.91692.623546.059
all_on20.59298.445550.035
Table 6. Breakdown of contract evaluation times by utility functions in the all_on configuration.
Table 6. Breakdown of contract evaluation times by utility functions in the all_on configuration.
Contract TypeName of Operation/
Utility Function
Average Execution Time (ms)
SmallMediumLarge
Preconditionrequire or ignore0.1780.1840.247
Postconditionold8.58913.65838.703
ensureAssignable10.81482.874497.745
ensure0.7281.36212.353
Class Invariantinvariant0.2830.3670.987
Total Contract Execution Time20.59298.445550.035
Table 7. Breakdown of the proportion of contract CPU time by utility functions in the all_on configuration.
Table 7. Breakdown of the proportion of contract CPU time by utility functions in the all_on configuration.
Contract TypeName of Operation/
Utility Function
Average Proportion of
Contract CPU Time(%)
SmallMediumLarge
Preconditionrequire or ignore≈0.000.050.04
Postconditionold97.0088.6760.89
ensureAssignable2.082.4614.48
ensure0.927.8123.39
Class Invariantinvariant≈0.000.821.16
Total Proportion of Contract CPU Time≈100.0099.8199.95
Table 8. Breakdown of the contract evaluation times for writing the precondition with (1) ObjectUtils, java.util, and ordinary Java code and (2) utility functions of uContract.
Table 8. Breakdown of the contract evaluation times for writing the precondition with (1) ObjectUtils, java.util, and ordinary Java code and (2) utility functions of uContract.
Contract TypeName of Operation/
Utility Function
Average Execution Time(ms)
SmallMediumLarge
(1)(2)(1)(2)(1)(2)
Preconditionrequire or ignore0.1610.2980.2030.3300.3130.451
PostconditionoldN/A0.014N/A0.013N/A0.011
ensureAssignableN/A0.027N/A0.024N/A0.025
ensureN/A0.262N/A0.241N/A0.235
Class InvariantinvariantN/A0.095N/A0.069N/A0.046
Total Time0.1610.6960.2030.6770.3130.768
Table 9. Ratings of the DbC tools by quality attributes.
Table 9. Ratings of the DbC tools by quality attributes.
DbC Tools (Language)Quality AttributesOverall Rating
(12 Points)
SustainabilityUsabilityPortablityDevelopability
uContract(Java)HighHighHighHigh/Medium12/11
godbc(Go)HighHighHighHigh12
Boost.Contract(C++)MediumHighLowMedium8
contracts(Rust)MediumHighLowMedium8
icontract(Python)HighHighLowMedium9
deal(Python)HighHighLowMedium9
decorator-contracts
(TypeScript & ECMAScript)
HighHighLowMedium9
Digital Mars C++ compiler(C++)LowLowMediumLow5
JML/OpenJML(Java)LowMediumMediumLow6
dbc4go(Go)LowMediumMediumLow6
Table 10. A list of DbC tools and their necessary contract translation processing tools by phases.
Table 10. A list of DbC tools and their necessary contract translation processing tools by phases.
DbC Tools (Language)Contract Translation Phases
PreprocessingCompilationPost-Processing
uContract(Java)N/Ahost languageN/A
godbc(Go)N/Ahost languageN/A
Boost.Contract(C++)host languagehost languageN/A
contracts(Rust)host languagehost languageN/A
icontract(Python)N/Ahost language
(interpretation)
N/A
deal(Python)N/Ahost language
(interpretation)
N/A
decorator-contracts
(TypeScript & ECMAScript)
N/Ahost languageN/A
Digital Mars C++ compiler(C++)N/AcustomN/A
JML/OpenJML(Java)customcustomN/A
dbc4go(Go)customhost languageN/A
Table 11. The attributes of each tool. A check in a cell means a tool has the attribute.
Table 11. The attributes of each tool. A check in a cell means a tool has the attribute.
DbC Tools (Language)Attributes
SCNSBLNIFDSASCOCATLDI
uContract(Java)VVVVV
godbc(Go)VVVVV
Boost.Contract(C++)VVVVV
contracts(Rust)VVVVV
icontract(Python)VVVVV
deal(Python)VVVVV
decorator-contracts
(TypeScript & ECMAScript)
VVVVV
Digital Mars C++ compiler(C++) V V
JML/OpenJML(Java)V VV
dbc4go(Go)V VV
Disclaimer/Publisher’s Note: The statements, opinions and data contained in all publications are solely those of the individual author(s) and contributor(s) and not of MDPI and/or the editor(s). MDPI and/or the editor(s) disclaim responsibility for any injury to people or property resulting from any ideas, methods, instructions or products referred to in the content.

Share and Cite

MDPI and ACS Style

Chen, C.-T.; Yen, Y.-C.; Hu, Y.-H.; Cheng, Y.C. Root Contracting: A Novel Method and Utility for Implementing Design by Contract in Domain-Driven Design with Event Sourcing. Electronics 2025, 14, 4205. https://doi.org/10.3390/electronics14214205

AMA Style

Chen C-T, Yen Y-C, Hu Y-H, Cheng YC. Root Contracting: A Novel Method and Utility for Implementing Design by Contract in Domain-Driven Design with Event Sourcing. Electronics. 2025; 14(21):4205. https://doi.org/10.3390/electronics14214205

Chicago/Turabian Style

Chen, Chien-Tsun, Yi-Chun Yen, Yu-Hsiang Hu, and Yu Chin Cheng. 2025. "Root Contracting: A Novel Method and Utility for Implementing Design by Contract in Domain-Driven Design with Event Sourcing" Electronics 14, no. 21: 4205. https://doi.org/10.3390/electronics14214205

APA Style

Chen, C.-T., Yen, Y.-C., Hu, Y.-H., & Cheng, Y. C. (2025). Root Contracting: A Novel Method and Utility for Implementing Design by Contract in Domain-Driven Design with Event Sourcing. Electronics, 14(21), 4205. https://doi.org/10.3390/electronics14214205

Note that from the first issue of 2016, this journal uses article numbers instead of page numbers. See further details here.

Article Metrics

Back to TopTop