Day 1 (Part 2) - DTOs, Global Exceptions Handling and Extension functions

Olaoluwa Oke| 20 June 2025

I don’t know how I went this long without hearing the term DTO. It stands for Data Transfer Object, but somehow i had not even heard of the concept The idea is simple: when data moves between layers like from a controller to the frontend you wrap it in something data appriopriate ( okay that probably didn;t help) It kinda just makes sure that you aggregate the data been transferred by several calls to the server(which can be expensive btw) in one call- They are only good for serialization and deserialization

I made four for my room entity
RoomCreateRequest JoinRequest RoomResponse RoomUpdateRequest


At first, I wanted to store a list of users inside each Room—List<$User> participants>>. It felt intuitive. But after reading the MongoDB docs and some community threads, I saw the risks: the 16MB document limit and tightly coupled data. If user info changed, every Room they belonged to would need updating. Switching to userId references was simpler, cleaner, and easier to maintain.
Global Exception Handling
One of the first things I built in this project was error-prone. Not because it was poorly written (though maybe it was), but because I didn’t yet know how to fail well.

Every time something went wrong like wrong ID, missing field, duplicate data, I would throw raw exceptions. And they’d explode across the console like broken glass.It worked, technically. i just needed to upload the entire crash message to chatgpt to parse for me

So I built a GlobalExceptionHandler using @ControllerAdvice. to make it at least, Readable
DTOs and Service Layer
So far , we have come from Zero to a room entity , Well almost the same process for user entity , but i will write that as well

Once I had the DTOs in place, the next step was to give them a job. Well technically, I had written them because I already had a job for them. Drumroll please... yes: Data Transfer.

Shocking, I know.

Somewhere along the way, I learned that exposing your entire model to the outside world is a bad idea. It's like handing someone your entire house keychain when all they asked for was the garage. That’s what DTOs solve. You send out just what’s needed

Which brings us to the next stop:
“sErViCe lAyEr iS wHeRe yOu wRiTe bUsiNeSs rUlEs” — This guy on YouTube


Well, yes. This is where the actual decisions live. The RoomService became the part of the codebase that does things. Want to create a room? Edit one? Join one? Get the details of one? That’s all here.

Controllers

Controllers - you know , Air Traffic ..... yes , Air Traffic Controllers help direct planes and tell them what runway or where to go generally
These guys:

ERD image


Well here too , Our controller , or more specifically , RoomController , tells any request (associated with room) what runway to land
You want to create a room? Take runway /api/rooms
“Trying to join with an invite code? Proceed to /api/rooms/join.”
“Just want to look up a room’s info? That’s gate /api/rooms/{id}.”

I exposed routes
ERD image


Testing


Did I test these routes? Yes. I used Postman.

->Created a room
->Joined using a valid invite code
->Tried an invalid code
->Tried joining the same room twice
->Updated the room name

All the expected behaviors worked. Error handling also returned the correct status codes (404 for not found, 409 for duplicate join).

Did I write unit tests? No. I don’t know how to write them yet. That’s something I plan to learn after authentication is done. Right now, I’m relying on manual testing to validate logic.

See you on Day 2