Agile Java weaves all test-driven development, object-oriented design, and software architecture into a single, clean approach for building robust and highly scalable software systems.
Today, you will learn how to implement different relational models using the Spring framework. We’ll show you how to use a test-driven and agile development approach. These popular methodologies help us design and develop robust and manageable applications.
Here’s what we’ll cover today:
Learn all 15 types of domain models in Java with interactive coding examples and professional advice.
Agile development is a new methodology taking the industry by storm. This software development methodology allows us to build a program incrementally with short iterations over 1 to 4 weeks. The incremental procedure helps align the development process with business needs.
Agile development is an iterative methodology for software development that focuses on creating adaptive products. Projects take multiple iterations that each builds on the previous iteration and gradually approach the final product.
Agile methodologies also prioritize close, in-person collaboration between development teams and business teams at all stages. Developers reassess business requirements and development practices at the end of each iteration. The findings are used to adapt the product in the next iteration. Agile also welcomes new feature ideas that can be added at any iteration, even in the late stages.
Traditional development only accounts for business requirements at the beginning of the process, which means that the product may no longer be optimal or meet business needs at the end of the development cycle.
Test-driven development (TDD) is a key development technique that makes agile development possible. TDD focuses on gradual development by creating and testing small pieces of code during development. If all small pieces of code pass their tests, the project is ready to ship.
TDD perfectly compliments agile development because it ensures each feature works before moving on to the next. Developers can then combine all working features at the end of an agile iteration to form a working project component.
The TDD process has 4 steps:
With TDD, we can take small measurable steps toward completing a larger project. Rather than feeling overwhelmed by the large task “create a web app”, you can break it down into small steps like “create a sign-in button”, “display the newest content”, etc.
TDD is also helpful for debugging. Each cycle of the TDD process involves less code than a traditional coding process. It’s much easier to check a 10 line feature for bugs than a full 1,000 line project.
When using agile and test-driven development, it’s best practice to model solutions using domain models. This will help everyone on the team understand the architecture of the program and how their components will be connected in the final product. Below, we’ll explain what a domain model is and give you some examples of how to represent these models in your code.
Software architecture refers to the fundamental structure of a programming solution. This architecture is often created early in the development process. It serves as a plan for how the program pieces should be designed and interact with each other.
Think of domain Modeling as a way to describe the real-world entities of your program and the relationships between them.
Agile developers favor domain modeling for its real-world applications and team-based planning. Domain modeling is a conceptual model that tracks both the behavior and data of a project segment. Each of these segments are called “domains” and represent spheres of real-world knowledge or behaviors within the code.
These domains are then connected to all other relevant domains in the model. Once completed, the domain model allows developers to track what behaviors they need to include and what interactions each team will plan for.
Domains are usually represented as rectangles connected to other linked domains through a network of arrows. In Java, domains are represented through classes.
Agile developers use this model because it allows all project members to understand the purpose and connection of each project component. The domain model also keeps code out of the planning process. Instead, developers only start considering code once it’s time to write the domain function. This allows developers to program each domain to fit all previously created project parts and apply more current information gathered throughout the project cycle.
Each domain has different strengths and weaknesses and is suited for specific types of projects. The five most common domain models are standalone entities, one-to-one unidirectional, one-to-one self-referencing, one-to-many bidirectional, and many-to-many.
Understanding Domain Model Names:
The first section of most domain names refers to how many entities are linked to each other and if there is a parent entity, like with “one-to…” relationships. The second part explains the direction of link access. In unidirectional, access only goes one way, whereas bidirectional goes both ways.
Each model requires the same 7 general steps to create. Let’s dive into them.
Each of these steps requires slightly different code implementations depending on the chosen domain model type. Later in the article, we’ll explore how the creation of a data transfer object differs for the top 5 domain models.
To successfully run Java domain models, you’ll need 5 tools. If you don’t want to download anything right now, feel free to skip to our example section featuring embedded code.
The tools you’ll need are:
You’ll also need source code zip from our Spring-Hibernate book on Github.
You’ll then need to unzip these files and import them into your Eclipse as a Maven project.
After choosing the Existing Maven Projects option, select the folder where you have unzipped the downloaded file and choose the directory containing the POM as shown below.
From here, use MySQL to create a new schema named spring
in the MySQL Workbench.
Next, open Tomcat and install a v7.0 server. Then, click Add Library -> JRE System -> Library -> Alternate JRE -> Installed JREs. Set this as your default then click Finish.
Finally, add the Spring OODD project to the Tomcat configuration. Once started, Tomcat will load the index page from web.xml
.
Congratulations, you now have the workspace to implement Java domain models!
Next, we will discuss five common domain models and teach you how to create a data transfer object for each.
Learn to implement Java domain models in your own programs. Educative’s text-based courses offer live coding environments so you can apply your learning immediately.
This model features a single entity with no relationships. It is used to demo individual entities before adding them to relationship models. Developers use a standalone entity to look at the individual components of a Spring application and understand the role they play.
For example, we can look at an entity Product
with a String field, name
.
Entity vs Class
A class is a technical representation of a collection of related data. An entity is a collection of data that represents a real-world object or function. In Java, classes that represent real-world concepts or behaviors are entities.
To create a Data Transfer Object for a Standalone Entity, create a class with the following code:
public class ProductDto implements Serializable {
private static final long serialVersionUID = 1L; private Integer id;
private String name;
// getters and setters
}
Here the implements
keyword maps this object to the user interface. Each variable is private to avoid disclosing the database design to the user interface.
One-to-one unidirectional models involve two individual entities where data and service can move in only one direction. Once data is sent to the child object, it cannot refer back to the parent, as there is no link to the parent within the child.
For example, imagine we have a bookstore shipping program represented by a one-to-one unidirectional model with two entities, Book
and Shipping
. The Book
entity has an identifier and a String name
field, Similarly, the Shipping
entity has an identifier and a String city
field. An instance of Shipping
must be present in an instance of Book
. The Book
is the owning side of the relationship. It may traverse Shipping
, but Shipping
cannot traverse Book
.
This is used for situations where the first stage of a process does not need to know the outcome of the second stage. For example, the Book
entity above does not need to know what’s happening in the Shipping
entity, like what city the book is going to. The Book
entity simply needs to know that a certain book was passed to Shipping
so it can be removed from inventory.
To create a Data Transfer Object in a One-to-one unidirectional relationship, you’ll use the following BookDto
class:
public class BookDto {
private Integer id;
private String name;
private String city;
// getters and setters
}
Again, we encapsulate the entity design using private variables to prevent the client or UI from viewing unneeded information.
The data transfer object contains an identifier of the Book
object, a name, and a city field. However, it does not have the identifier for the Shipping
Entity. While Book
and Shipping
are related in the service, the user sees only the Book
object via the data transfer object.
This architecture will help your user interface team down the line, as they do not have to manage the Shipping
entity identifier and can just call the service as one object.
In a one-to-one self-referencing relationship, an entity is linked to one other instance of the same class. Because it is one-to-one, an instance cannot link to more than one other instance. However, not all instances in this model need to have relationships.
For example, imagine a relationship between a Student
entity and a mentor who is also an instance of Student
. EachStudent
entity has an identifier and a String name field. The Student
entity can have an optional mentor who is also a Student
. This association is declared in the first instance of Student
, not within the mentor Student
.
This is best used when there are relationships between instances of the same class that are too similar to need a separate class. This also makes it ideal for situations where relationships are optional, not standard.
For a one-to-one self-referencing relationship, our data transfer object will look like:
public class StudentDto {
private Integer id;
private String name;
private String mentorName;
// getters and setters
}
Notice that we have the id
, name
, and mentorName
but do not include the mentor object identification.
The data transfer object does not have the identifier of the Student
mentor object because the UI team will not need to see the internal design of our relationship system. We only include a single mentorName
, as each Student
can only have a single mentor.
A one-to-many bidirectional relationship exists when a parent entity is associated with any number of instances of a child entity, and each child entity contains a link back to the parent entity. Communication can go either from parent to child or from a child instance to the parent because both sides must have links to the other.
For example, consider a program that lists the features of an item with two entities Item
and Feature
. The Item
entity has an identifier and a String type name
field. The Feature
entity also has an identifier and a String type number
field. Each instance of Item
must contain a link to at least one. Similarly, each instance of Feature
must link to one instance of Item
.
As the relationship is bidirectional, all instances of either Item
or Feature
can access any instance linked to it.
This is best used when you want to reuse child instances across multiple parents in complex relationship networks. In our example above, a parent instance of Item
may link to the child feature Waterproof
, but waterproof also links to many other instances of Item
. What’s more, our Item
can link to as many features needed to describe it.
For a one-to-many bidirectional relationship, our data transfer object will look like:
public class ItemDto {
private Integer id;
private String name;
private List<String> featureList;
// getters and setters
}
The Item
data transfer object has an identifier, name of the Item
, and a list of the String Feature
names. Using a list for the Feature
names allows us to link multiple Feature
objects to our one Item
.
We exclude the identifier for the Feature
object in the data transfer object to simplify the API call for the UI team. Not only does this keep the database design secure, but it also saves the UI team the trouble of managing the identifiers of each child object.
In a many-to-many unidirectional relationship, a parent instance is linked to any number of child instances and child instances can be linked to any number of parent instances. Parent instances can access its child instances. However, the child instance is not allowed to access its parent.
For example, imagine you’re creating a simple social media program that allows a parent entity, User
, to link with a child entity, Group
. The User
entity has an identifier and a String type name field. The Group
entity also has an identifier and String type name field.
Every many-to-many relationship has a parent. In this example, the User is the parent. Each instance of User
is linked to at least one instance of the Group
. Many instances of User
can be linked to the same instance of Group
.
An instance of User
can access data within all linked Group
instances, however, Group
instances cannot access data within the User
instance.
This is best used to securely create complex networks where some data is public and some are sensitive.
For the many-to-many unidirectional relationship, we need to create three data transfer objects.
public class UserDto {
private Integer id;
private String name;
// getters and setters
}
public class GroupDto {
private Integer id;
private String name;
// getters and setters
}
public class UserGroupDto {
private UserDto userDto;
private GroupDto groupDto;
// getters and setters
}
The UserDto
data transfer object has an identifier and the name of the user. Similarly, the GroupDto
object has an identifier and the name of the group. The third object, UserGroupDto
, is a join object and connects UserDto
and GroupDto
. UserGroupDto
, has an instance of UserDto
and GroupDto
within and can be used to call both transfer objects together.
Congratulations! You’ve taken your first step toward implementing each of the top 5 domain models used in agile architectural planning.
From here, you’re ready to explore the 10 more advanced domain models. You can also work beyond the initial data transfer object step covered to create your own fully functional architecture.
Educative’s course Software Architecture in Java: Design & Development covers all 15 domain models used in modern programming and teaches you how to implement each in your own programs. By the end of the course, you’ll be able to apply each domain model to your work and have learned agile best practices to aid both your development and UI teams.
Happy learning!
Free Resources