Data Layer Architecture
The data layer follows a three-tier pattern: Parser → Repository → Service. Each tier has a single, well-defined responsibility.
Pattern Overview
| Tier | Responsibility | Should touch DB? |
|---|---|---|
| Parser | Read XML / SQL source, produce domain objects | No |
| Repository | Hold in-memory data, expose query methods | Only for initial load |
| Service | Runtime logic — DB writes, item manipulation, state transitions | Yes |
// Loading sequence at startup:
Parser.load() // reads XML, creates objects
└─► Repository.init() // stores into indexed maps
└─► Service (called at runtime, not startup)
Concrete Examples
Items
ItemParser → parses items XML, creates ItemTemplate instances
ItemRepository → Map<Integer, ItemTemplate> by item ID, query by type / grade
ItemService → create L2ItemInstance, equip, transfer, DB persist
NPCs
NpcParser → parses NPC XML, creates NpcTemplate instances
NpcRepository → Map<Integer, NpcTemplate> by NPC ID
(no NpcService needed — NPC spawning handled by SpawnTable)
Skills
SkillParser → parses skill XML including effects and conditions
SkillRepository → Map<Integer, Map<Integer, Skill>> by skillId + level
SkillService → cast, reuse timers, buff persistence
Repository Design Conventions
- Repositories are singletons —
getInstance()or staticinit()/get(). - All maps are populated once at startup and treated as immutable at runtime.
- Query methods are named descriptively:
getById,getByType,getAll. - Return types are explicit — never return raw
Objector unchecked casts.
Parser Design Conventions
- Parsers use the SAX or StAX API — no DOM for large XML files.
- A parser produces only plain Java objects — no DB calls, no side effects.
- Parsing errors log a warning and skip the offending entry. They do not crash startup.
public class ItemParser {
public List<ItemTemplate> parse(String xmlPath) {
// SAX parse, return list
}
}
public class ItemRepository {
private static final Map<Integer, ItemTemplate> ITEMS = new HashMap<>();
public static void init(List<ItemTemplate> items) {
items.forEach(t -> ITEMS.put(t.getId(), t));
}
public static ItemTemplate getById(int id) {
return ITEMS.get(id);
}
}
Adding a New Data Loader
- Create
MyDataParser.java— takes a file path, returns a list of domain objects. - Create
MyDataRepository.java—static init(List)+ query methods. - If runtime DB interaction is needed, create
MyDataService.java. - Call
MyDataRepository.init(new MyDataParser().parse("data/mydata.xml"))fromGameServerduring the data loading phase.
If the data does not require runtime DB writes — for example, static reference data like item templates —
a Service tier is not needed. Parser → Repository is sufficient.