Stop Confusing PO, VO, BO, DTO, DAO, and POJO – A Complete Guide

This article clarifies the roles and differences of PO, VO, BO, DTO, DAO, and POJO in Java backend development, showing where each object belongs in the architecture, providing concrete code examples, and offering practical advice to keep layers clean and maintainable.

Top Architect
Top Architect
Top Architect
Stop Confusing PO, VO, BO, DTO, DAO, and POJO – A Complete Guide

When I first started working, I kept seeing classes named xxxPO, xxxVO, xxxDTO and felt completely lost. After searching online I found contradictory explanations, so I decided to explain all these concepts in one place.

POJO – the ancestor

POJO stands for Plain Old Java Object . It is simply a regular Java class that does not extend any framework class or implement any special interface – just private fields with getters/setters.

public class User {
    private Long id;
    private String name;
    private String email;
    // getter and setter omitted
}

All the other objects discussed below are specializations of a POJO.

PO – Persistent Object

PO maps one‑to‑one with a database table. Every column in the table becomes a field in the PO.

public class UserPO {
    private Long id;
    private String name;
    private String email;
    private Date createTime;
}

POs are used only for database interaction; they should not contain business logic or fields that are not present in the table.

DAO – Data Access Object

DAO is an interface (or class) that encapsulates CRUD operations for a PO. It works together with the PO; the method signatures usually accept or return PO instances.

public interface UserDAO {
    UserPO findById(Long id);
    List<UserPO> findAll();
    void insert(UserPO user);
    void update(UserPO user);
    void deleteById(Long id);
}

Modern frameworks such as MyBatis‑Plus or JPA can generate the DAO implementation automatically, but the concept remains important for understanding the data layer.

DTO – Data Transfer Object

DTO is used to carry data between layers or services. It should contain only the fields needed for the transfer and can reshape or omit fields from the PO.

Using a PO directly as a DTO is a common mistake; for example, exposing a password field would leak sensitive data.

// PO (database structure)
public class UserPO {
    private Long id;
    private String name;
    private String password; // sensitive
    private Integer status;
    private Date createTime;
}

// Proper DTO (sent to front‑end)
public class UserDTO {
    private Long id;
    private String name;
    // password omitted
    private String statusDesc; // status converted to description
    private String createTimeStr; // formatted date string
}

DTOs allow “cut‑by‑need” transformations, keeping the API contract independent from the database schema.

VO – View Object

VO is the object that the controller returns to the front‑end for display. It is tailored to the UI needs and may combine or format data differently from the PO.

public class UserVO {
    private Long id;
    private String name;
    private String avatar; // full URL
    private String roleName; // role name, not roleId
    private String createTime; // formatted string like "2026-04-10"
    private Boolean isVip; // boolean for front‑end use
}

BO – Business Object

BO lives inside the service layer and aggregates data from multiple tables for complex business scenarios. It is never exposed outside the service.

public class OrderBO {
    private UserPO user;
    private List<ProductPO> products;
    private CouponPO coupon;
    private BigDecimal totalAmount;
    private BigDecimal discountAmount;
    private Boolean isEligibleForDiscount; // intermediate result
}

Layer relationship

Front‑end request
    ↓
Controller layer → receives DTO (from front‑end), returns VO (to front‑end)
    ↓
Service layer → uses BO for internal business processing
    ↓
DAO layer → uses PO to interact with the database (CRUD)
    ↓
Database

In practice, many small projects blur these boundaries, but as the codebase grows, mixing them leads to maintenance headaches. A recommended practice is to keep PO and VO strictly separate, let the controller return only VO, keep DAO paired with PO, and introduce BO or DTO only when the situation demands.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaArchitectureDTODAOPOJOVOBO
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.