To Build Omnilude, This Is the Backend I Am Building
This post is not about introducing a single feature. It is a post about what kind of structure the project called Omnilude is being built on, and why I am organizing the backend in this way.
What I am doing right now overlaps in two directions. One is testing the limits of coding agents. I keep checking how far implementation can go with instructions that are close to vibe coding and stream-of-consciousness direction, and whether that approach can truly extend to production-level development. The other is making sure that experiment does not remain just an experiment, but eventually turns into a real service. Omnilude sits at the center of that service, and on top of this structure I want to push the experiment forward through the kind of game development I had wanted to do before.
So this post is closer to a structural introduction than an implementation showcase. I also did not want to explain it by simply listing service names. This backend is split into several modules, but the way I actually look at it is slightly different. I see the whole thing as a single product backend. Authentication, AI execution, content management, real-time connections, file storage, and event streams are all bound together toward one goal.
This may turn into a long and difficult post, but I want to explain it as concretely as possible.
What Kind of Project Is Omnilude Trying to Build
Omnilude means two things to me at the same time.
First, it is an experiment. At a time like this, I do not think it is enough for developers to simply try new tools. The gap between people who use AI well and people who do not will probably continue to widen, and I believe that gap will soon be driven less by implementation speed and more by problem definition, structural design, and validation. I did not want to just observe that change. I wanted to pass through it with my whole body while building an actual product.
Second, it is a very practical product goal. I have wanted to make games for a long time. Now I do not want to approach that goal only in the old way. I want to approach it by tying together AI-powered production tools, a content generation pipeline, an operable backend, and even real-time experience into a single flow. Omnilude sits directly on that path.
In other words, this project is both an AI agent experiment for making the most of AI and a product development project that ultimately has to converge on a service that can actually be launched.
Multiple Services, Viewed as One Product Backend
As of the moment I am writing this, the Omnilude backend is still a single monolithic service. But I do not see that monolith as just one large server. I see it as a structure where multiple responsibility boundaries coexist inside it. The auth-service, ai-service, blog-service, game-service, storage-service, backbone-service, mmorpg-service, and the shared foundation common(jspring) that appear in this post are less about today''s deployment units and more about the way I understand and extend this system. In this post, I will refer to that internal shared layer simply as common.
So when I say multiple services, but viewed like one product backend, I am not trying to exaggerate the current structure. It is closer to a way of explaining which responsibilities are already separated inside a system that is still attached together, and in what direction those boundaries can become sharper over time. What matters is not the number of services itself, but whether each responsibility is tied toward one product on top of a shared set of conventions.
Rather than seeing today''s Omnilude as just one giant server, I see it as a structure where multiple boundaries coexist inside one product backend. I want to document, in a separate post, how those boundaries are later separated into real repositories and deployment units. In this post, I will unfold the responsibility boundaries that already exist inside the current monolith using the terms I use today.
The diagram below is an overview of the current non-legacy backend, regrouped and viewed as a single product backend.
The three points I care about most in this diagram are these.
First, common forms the floor of the entire system. Each service exists independently, but authentication conventions, internal HTTP call patterns, event publishing paths, storage handling, and distributed job processing all repeat on top of a shared foundation. That is what keeps the structure from scattering.
Second, product domains and operational infrastructure are separated. auth, ai, game, mmorpg, and blog represent product features, while storage and backbone handle the common operational foundation those product features need in order to run in the real world.
Third, this separation is also what makes the structure better suited for AI. Coding agents do not handle a giant codebase well when there is no context boundary. They operate much more reliably in a structure where roles are separated and conventions are explicit. I am designing this structure not only so that humans can maintain it more easily, but also so that AI has a better environment to work in.
common Is at the Center of This Structure
The first thing that has to be explained when trying to understand this project is common, because it is ultimately the reason the whole system can move like one product.
common is not just a collection of utilities. I see it as something closer to an internal platform SDK. If you look at how the services connect to each other in practice, a lot has already been standardized inside common.
common-coreprovides the lowest-level foundation such as JWT, shared exceptions, and basic utilities.common-webprovides the internal HTTP client and shared web-layer conventions.common-databundles JPA, QueryDSL, and the shared data access foundation.common-messagingunifies the path for publishing events.common-cloudstandardizes file-handling integrations such as storage commits and access URLs.common-dteprovides the flow for distributed jobs, workflows, and job events.
The reason I think this layer matters is simple. In AI-first development, what matters more than how quickly code gets written is how much repeatable pattern the system already has. If every new feature requires either a human or an agent to understand a completely different structure, productivity drops quickly. If the shared foundation is strong, the problem can be broken into smaller units, and the review points become much clearer.
For example, when game-service calls AI functionality, it does not attach itself directly to external models without structure. It calls ai-service internally. File storage is also organized so that each service does not handle S3 on its own, but instead goes through storage-service. Events, when needed, flow around backbone-service. Because there is this kind of consistency, I can give AI more accurate instructions and validate the results much faster.
This structure also matters from a developer''s perspective. Even if it looks like there are many services, they actually share the same language, the same framework, the same conventions, and the same internal platform. That means the unit of thought does not fragment completely. I think that is the biggest strength of the Omnilude structure.
Why the Product Domains Are Split This Way
Now I want to explain a bit more concretely what each service is responsible for. More than listing service names, it matters why they are split that way.
auth-service: The Starting Point of Trust
auth-service is responsible for login, accounts, devices, permissions, credits, and activity logs. On the surface it may look like a fairly familiar authentication server, but I do not think of it as just a login API. This service defines the trust boundary of the entire product.
Who the user is, what session they have, what permissions they hold, and what credits or usage history they have all mostly start here. If other services are going to experiment and ship features faster, this foundational trust layer has to be more conservative and more explicit, not less.
What is interesting is that this service is not completely isolated. At startup it synchronizes platform settings with backbone-service and helps shape the shared JWT convention across the backend. In other words, authentication is both an independent feature and a shared language that connects the whole system.
ai-service: The Hub of AI Execution
ai-service is the most symbolic service in this project. LLM calls, multi-agent workflows, media generation, product agents, roleplay, and various system-level AI APIs are gathered here.
The important point is that this service is not just a proxy for external model APIs. I see it as an AI execution hub. It is the central layer that decides which model to call, what workflow to go through, what events to emit, what outputs to store, and how progress should be streamed outward.
I think this structure will matter even more going forward. In projects that use AI aggressively, once external model calls start spreading everywhere, they become difficult to control almost immediately. If you think about cost, failure handling, prompt conventions, execution logs, output storage, and provider replacement strategy, it is better for AI to be coordinated in one place. That is the role ai-service is taking on.
game-service: The Domain I Want to Make Real First
game-service is the core product domain of this project. Scenario generation, story quiz, game sessions, shared game metadata, and asset studio functionality are gathered here.
The reason this service matters is simple. What I want to launch first in Omnilude ultimately connects back to this domain. That is why this service is designed not as a simple data store, but as a domain that interacts with AI more than any other. Scenario creation, asset generation, session creation, and play-flow operations all converge here.
What matters especially is that game-service is directly connected to ai-service. Content generation, review, support tasks, asset production, and some automation flows will inevitably be accelerated through AI. Rather than hiding that connection, I thought it was better to surface it structurally.
mmorpg-service: Real-Time Runtime as a Separate Axis
mmorpg-service belongs to the same game area, but its nature is very different. This service does not just provide HTTP-based management APIs. It also runs a real-time WebSocket runtime on a separate gateway port.
If someone asks why I did not mix this into the same service, the answer is clear. CRUD-oriented content APIs and real-time game servers have completely different failure modes, performance requirements, and state-management patterns. I think it is more correct not to treat the two within the same unit of thought.
The authentication pattern is also interesting. At connection time, the JWT convention issued by auth-service is validated locally inside the mmorpg-service gateway. In other words, the runtime does not call the auth service again over HTTP every time. This structure shows well where a real-time system should diverge from ordinary business APIs.
blog-service: The Project''s External Memory
blog-service may look relatively less complex, but it has an important place for me. It is not just a CMS for running a blog. It is the channel through which I accumulate, externally, what I am building, what decisions I am making, and what experiments I am running.
If Omnilude is not just a project that stays internal, its structure, experiments, and trial and error have to be explainable from the outside. blog-service is responsible for that record. That is why posts, comments, translations, SEO, and static asset links gather here. The product needs an external communication layer.
Why the Operational Infrastructure Is Built Separately
A service does not run on product features alone. You need layers that store outputs, manage file lifecycles, stream progress, carry events, track long-running jobs, and maintain real-time connections.
In Omnilude, storage-service and backbone-service mainly take on that role.
storage-service: The Real Lifetime of Files Is Managed Here
In many projects, file storage is treated like a feature that gets bolted on later. But in a real product, file upload, temporary state, commit or cancel, access URL, thumbnail URL, and static object metadata all matter. Files are not just uploaded and forgotten. They have a lifecycle.
That is why I separated storage-service. Whether it is blog images, outputs created by AI, or game assets, I tried to integrate them into this path as much as possible. That way, no matter which service handles a file, it can operate on top of the same rules. Another advantage is that if the access policy or storage strategy changes later, the impact can be contained.
backbone-service: The Center of Operations and Asynchrony
backbone-service is, true to its name, close to the backbone. The event hub, SSE, WebSocket, platform settings, and DTE job event flow gather here.
This service matters especially because it fits tightly with AI execution. Many of the features I am building do not end with a single synchronous API call. Once the user starts a task, you need intermediate progress, a completion event, and in some cases a real-time connection or notification. If each domain service starts solving those demands separately, the structure gets messy very quickly.
That is why I gathered asynchronous delivery and the operational real-time layer into the backbone. I see a structure where the default event path is organized as HTTP -> backbone -> Kafka/RabbitMQ, and where DTE-related progress flows through SSE, as a much more manageable approach.
The flow below simplifies what an AI generation task looks like in practice.
The reason I like this structure is clear. ai-service owns generation, storage-service owns output storage, and backbone-service owns outward delivery of progress. Because the responsibilities are separated, each part can be validated much more clearly.
How the Data Stores and Messaging Layer Are Used
To see the structure more three-dimensionally, you also have to look at the storage and messaging layers. The current non-legacy Omnilude backend does not throw infrastructure together indiscriminately. The role of each layer is fairly clear.
- PostgreSQL is the default persistent store for almost every service.
auth,ai,blog,game,storage,backbone, andmmorpgall share this axis. - Redis is used less as a simple cache and more for Pub/Sub and session-oriented event delivery. It is spread widely across services, but its purpose is fairly strategic.
- Cassandra is currently closer to the activity-log and event-write layer for
auth-serviceandbackbone-service. There is a boundary so that not every service attaches to it indiscriminately. - Kafka and RabbitMQ are handled in practice with
backbone-serviceat the center. Rather than each service attaching directly to a message broker, services first send events to backbone, and backbone propagates them to the external messaging layer. - S3 is handled directly by
storage-service, and other services go through it when possible. This is a choice made to gather file-handling rules in one place.
At first glance this can look a bit indirect. But I think this kind of separation matters much more in the long run. Once feature services start taking direct responsibility for every store and messaging path themselves, responsibility boundaries blur quickly over time. If infrastructure access is organized through gateway-like services like it is now, it becomes much easier later to change operational policies or narrow down failure points.
This structure is also advantageous when you work with AI. If it is clear where a given feature handles work directly and where it has to go through a shared path, the results you get from an agent become much more stable.
Why Authenticated Requests and Real-Time Connections Follow Different Paths
To understand the Omnilude structure more concretely, it helps to look at synchronous requests and real-time connections separately.
Typical content queries or management tasks mostly follow a flow where a user logs in through auth-service and then calls a domain API. In that case, what matters is whether the shared JWT convention and permission model are preserved. Because blog-service, game-service, and ai-service share those conventions, the user gets a relatively consistent experience even though services are separated.
Real-time connections, however, have different requirements. mmorpg-service, in particular, uses a separate gateway port at :9088/ws. The user already has a token issued by auth-service, and the gateway validates it locally. The structure is designed so that a session can be established quickly without a round trip to the auth server every time.
The flow below shows that difference.
This difference matters more than it may seem. Through this structure, I wanted to make it explicit that ordinary business APIs, long-running AI jobs, and real-time game sessions each need a different center of gravity. Making everything uniform can look simple on the surface, but in real operations it often makes problems worse.
How This Structure Connects to AI-First Development
By this point, a question may come up. Why does a structure like this connect to using AI well?
I think that question matters a lot, because many developers still see AI as little more than a tool that writes code on their behalf. But I think the point where productivity really jumps is not when you simply generate more code. It is when the system itself starts to have a structure that AI can understand well.
For example:
- A bundle of services with separated responsibilities is better for prompt decomposition than one giant service with blurry roles.
- A structure with an internal platform like
commongives agents a much more stable context than a codebase with no shared conventions. - When file handling, event handling, AI execution, and real-time connections are separated, it becomes easier to track where a problem is happening.
- The points a human has to review at the end also become much clearer.
In the end, AI-first development is not about attaching more AI everywhere. It is closer to organizing the system so AI can work more consistently. That is the exact point I am experimenting with while building Omnilude.
From that perspective, I think what developers need right now is training more than model news. Not just training in how to write better prompts, but training in how to build better structures, review with better standards, and design the context in which AI can work. I believe this project ultimately has to become the result of that training.
The Destination Is Still Launching Omnilude
There is one reason I did not want this post to stop at architecture alone. This structure is not a structure for explanation. It is a structure for building something that will eventually be released for real.
What I am doing right now is building AI-based production tools and implementing a game platform on top of them. This backend is being designed so those two flows meet. AI pushes content production forward, the game domain turns that into product experience, and the operational infrastructure pulls the whole thing up to a level that can survive as a real service.
I still want to launch a well-made game. And I want to verify for myself whether that process can become possible not only through instinct and enthusiasm, but through better structure, faster experimentation, and more precise validation. Omnilude is the project where that expectation comes together for me.
So going forward, I do not plan to write only about architecture on this blog. I want to keep documenting what features are actually being added, how far AI is pushing things, where human judgment still matters, and whether this structure truly leads to a product that can be launched.
Right now I am introducing the backend structure, but the more important question in my mind is this: can this way really go all the way to the end?
I am not going to answer that question with words alone. I am going to find out by actually building Omnilude.