Equality Lab is a Java and Spring Boot engineering lab for one deceptively hard question: when should two objects be treated as the same Java value, and when are they only related inside the business domain?
The first scenario uses a financial party hierarchy with party, individual, employee, trader, manager, institution, and Hibernate proxy-like runtime types. The page is meant to answer the project in order: what the problem is, which domain objects create the ambiguity, how the web and Java pieces are built, and what each equality rule changes in collections and persistence.
End-to-end build scenario
The full project is a website feature plus a Java backend service. A user opens the Equality Lab page, selects domain objects, chooses an equality strategy, and runs the evaluation. The React workbench shows the scenario and the result tables, the Next.js route keeps the demo stable with a local fallback, and the Spring Boot API owns the Java-oriented equality strategy execution when it is configured.
That means the project is not only a static article. It is an end-to-end slice: frontend explanation, API boundary, Java equality rules, collection consequences, deployable Spring Boot pod, and production delivery direction.
Problem
Java can tell whether two references are the same object, but the business has to decide whether two records mean the same party, employee, role, or persisted row.
Scenario
A financial party hierarchy with person, employee, trader, manager, institution, and Hibernate proxy-like objects exposes the equality traps.
Build Shape
The portfolio page owns the React workbench, the Next.js route owns fallback and adaptation, and the Spring Boot pod owns Java strategy execution.
Outcome
The result is a focused engineering lab that connects equality rules to collections, persistence, API boundaries, CI/CD, and Kubernetes deployment.
1. Problem: "same" is not one thing
Java gives every object identity by default. That default is safe for memory identity, but business systems usually need stronger meaning: same customer, same employee record, same legal party, same persisted row, same role, or only related records. If all of those questions are forced into oneequals method, the model becomes fragile.
Equality Lab makes the hidden decision visible. A user selects an anchor object, chooses target objects, switches equality strategies, and sees what Java would do across ==, equals, hashCode,HashMap, HashSet, and Stream.distinct().
2. Domain scenario: financial party hierarchy
Uses legal entity keys such as tenant, country, registration number, or normalized SWIFT/BIC.
This hierarchy is useful because it exposes the real traps. A trader can be an employee. A manager can also be an employee. Two role objects can share an employee number but still represent different role records. A Hibernate proxy can represent the same row while having a different runtime class. An institution can use normalized business keys instead of generated ids.
3. Runtime boundary details
The frontend belongs in this website because it is part of the portfolio experience. The equality engine belongs in Java because the topic is Java behavior, Spring Boot production design, and JPA lifecycle safety. The final production shape is therefore a web feature plus a separate Java API pod.
The Next.js API route is intentionally more than a proxy. It keeps the demo stable with a local fallback, and when the Spring Boot service is configured it adapts the workbench payload into the backend pair-evaluation contract.
4. What the lab computes
- Whether the selected objects are the same Java reference.
- Whether
anchor.equals(target)andtarget.equals(anchor)agree. - Whether equal objects land in the same
hashCodebucket. - Whether
HashSet,HashMap, andStream.distinct()behave safely. - Whether the selected rule creates JPA id, Hibernate proxy, mutation, or inheritance risks.
5. Strategy matrix
| Strategy | When A equals B | Production risk |
|---|---|---|
| Java default | Only the same memory reference | Two objects with the same business meaning still look different. |
| Business key | Same tenant plus normalized party number | Changing the key after putting the object in a HashMap breaks lookup. |
| Role-strict | Same tenant, employee number, and concrete role | Prevents Trader and Manager from collapsing into one broad employee value. |
| JPA id | Same persisted database id | Null ids on new entities must never make two transient objects equal. |
| Proxy-safe JPA | Same persistent class and id after Hibernate proxy unwrapping | Avoids getClass-based false negatives when Hibernate creates proxy classes. |
6. Collection behavior
Equality Lab does not stop at equals. It shows what the decision does inside everyday Java structures, because most real bugs appear when an object becomes a key, set element, cache entry, stream value, or retry marker.
| Structure | Visible behavior | Why it matters |
|---|---|---|
| List | Keeps both objects | List does not remove duplicates unless you ask it to. |
| HashSet | Keeps one or two | Depends on equals and hashCode together. |
| HashMap key | Finds or misses value | Lookup needs the same hash bucket and equals match. |
| Stream.distinct() | Merges or keeps both | Uses equals through an internal distinct set. |
7. Spring Boot API shape
GET /api/equality-lab/strategieslists strategy options.GET /api/equality-lab/presetslists domain scenarios.POST /api/equality-lab/evaluatereturns equality, hash, collection, contract, and domain-relation results.GET /actuator/health/livenessandGET /actuator/health/readinesssupport Kubernetes probes.GET /actuator/prometheusexposes request and strategy metrics.
8. Failure modes the project exposes
- Parent-child inheritance equality that breaks symmetry
- Too-broad employee equality that merges different role records
- hashCode mismatch after equals returns true
- OR-based identity rules that break transitivity
- Hibernate proxy equality where getClass and proxy classes disagree
- Transient JPA entities with null ids accidentally comparing equal
- Mutable fields used inside hashCode while objects live in HashSet or HashMap
- Unnormalized business keys such as SWIFT code, tenant id, email, or customer number
9. Delivery plan
The deployment should match the existing Strato pattern: app repo produces an immutable image and supply-chain evidence, deploy repo owns Kustomize desired state, and Argo CD rolls the service into K3s.
- Build and test the Spring Boot API in GitLab CI.
- Build the container image with an immutable digest.
- Generate SBOM with Syft and scan with Trivy.
- Apply a release policy instead of blindly promoting every image.
- Sign the image digest with Cosign.
- Open a deploy repo merge request that updates the Kubernetes manifest.
- Let Argo CD reconcile the merged desired state into K3s.
10. Kubernetes shape
Deploymentrunsequality-lab-apias a non-root Spring Boot container.Serviceexposes the pod inside the cluster.ServiceMonitorscrapes actuator metrics.HPAscales under CPU and memory pressure.NetworkPolicylimits which pods can call the API.ArgoCD Applicationconnects the deploy repo path to the cluster.
11. Engineering takeaways
Equality Lab is small enough to understand, but deep enough to show senior engineering judgment. It connects a core Java language feature to domain design, inheritance, JPA, collections, API contracts, CI/CD, supply-chain checks, Kubernetes deployment, and observability.