Good and best practices in UE... [Architecture]

I have a basic architectural question.

I’m doing a project, but I’m afraid I’m going to redo the entire project for several reasons.

I prefer to apply good practices Hexagonal + DDD + Vertical Slice + Screaming.

If I apply the practices I mentioned, the problem is that C++ has 2 parts, both private and public. That already causes the folders to be duplicated twice, which causes the code to increase the structure x2. Although I can apply Hexagonal very subtly. (Although with the asynchronous response towards Blueprints it becomes another world).

I understand that there is no framework, since what there is seems to be the typical “fire” embedded MVC.

So is there any way to do this efficiently? That is, follow a standard?

The framework of Gameplay Framework in Unreal engine | Unreal Engine 5.3 Documentation UE5 seems a bit… well, how can I say it, old.

Can someone experienced in the field give me some advice?

I’m thinking of using this idea:

As we can see UE5 seems to have a good engineer in good practices. But “something” is missing. That +.

I’m going to illustrate this with a simple example of Character and Vehicles
For example:

Summary
- Character (folder)
--private (folder)
--public (folder)
-Vehicles (folder)
--private (folder)
--public (folder)

This seems simple, the problem is when you want to apply Hexagonal, with the 3 layers of abstraction. My question is, what should it be like?

For example:

YouProject/source/nameProject/

1 - Form A

Summary
- Character 
-- domain
---- private 
---- public 
-- application
---- private 
---- public 
-- infrastructure
---- private 
---- public 
- Vehicles
....

Or on the contrary:

2 - Form B

Summary
- Character 
---- private
-- domain
-- application
-- infrastructure
---- public
-- domain
-- application
-- infrastructure
- Vehicles
...

The problem is that I see this as too repetitive by having two public/private sections. I would choose form 2 although it is too tedious to repeat all that, I don’t know if I will have problems with it in the future, in principle I shouldn’t have them.

Many people without knowledge of architectures (90%) do this:
https://www.reddit.com/r/unrealengine/comments/xy0j5y/c_project_structure/

Summary
YourModules
  \- Public/Private
     \- AI
        \- Contexts
        \- Decorators
        \- Services
     \- Actors
        \- GameModes
        \- Pawns
        \-PlayerControllers
        \- <other game specific recurring subtypes>
     \- Components
        \- WidgetComponents
        \- PrimitiveComponents
        \- <other game specific recurring subtypes>
     \- Interfaces
     \- Framework

Note: At first glance this seems “neat and correct”. I’ll explain it.
Sorting all actors by actor type, or rather, sorting everything by one type in the same folder, this is not productive. If you have 100 Actors, having them all in one folder is chaos.

The way EPIC programmers do it in the Engine project is good practice, but it is not enough.It seems that they have only stayed at the beginning of good practices without continuing. It’s like a principle of good practices.

this is just my case.

Summary
YourModules
  \- Public/Private
     \- Pawn
          \- Character
              \- Humanoid
              \- Vehicles

There are people who don’t give it importance and say “it doesn’t matter as long as it works”, well, it is true that “the whole” application can be put in a single .cpp and work. But it’s not practical for me.

In the editor with blueprints, I would follow the same approach (I would already be repeating too much, for the private, the public + in the editor). Although it is front, I would also like to have it good.

90% of Indies leave because maintaining a UE5 project is complex.

So is there any way to do this efficiently? That is, follow a standard?
Can someone experienced in the field give me some advice?
What architecture do Epic programmers recommend?

I have come to a conclusion.

I give a very basic example model

1 - Form A (We avoid repeating private and public every time) Without vertical slice

Summary

youproject/source/nameProject/
Private: (modules/“Vertical slice” that we do not want to expose to other modules)
-------- domain
------------Character
--------------Character.h
--------------Character.cpp
------------ Items
-------------- item.h
-------------- item.cpp

-------- application
-------------- CharacterPlayerController (Player controller referring to the Character)
-------------- UCharacterController (No, it is not the controller like Controller/PlayerController, it is our controller to determine what things we show or not in view)
-------------- Interfaces
-------- infrastructure (everything related to the database)
-------------- DAO (Data access object) or what is the same access to the data in the databases (either to an API rest, from a driver…). h .cpp
-------------- DTO (Data transfer object)

public (modules/“Vertical slice” that we want to expose to other modules)
-------- domain
------------ StatusPlayer
-------------- StatusPlayer.h
-------------- StatusPlayer.cpp
-------- application

-------- infrastructure

2 - Form B (We duplicate for both public and private and this is too heavy) With vertical slice

Summary

youproject/source/nameProject/

  • Character
    -------- private (domain, application infrastructure) . cpp
    -------- public (domain, application infrastructure) . h
  • Items
    -------- private (domain, application infrastructure) . cpp
    -------- public (domain, application infrastructure) . h
  • Others
    -------- private (domain, application infrastructure) . cpp
    -------- public (domain, application infrastructure) . h



Note: Separation of responsibilities
------------ CharacterPlayerController (Player controller referring to the Character)
It handles the logic related to player input, such as movement, rotation, shooting, etc.
------------ UCharacterController (No, it is not a controller that extends Controller/PlayerController. This is a UObject called NameController that takes care of the logic related to the character’s view, like the HUD, menu, inventory, etc.)



Which one should I choose?

The advantage of form A is that you avoid repeating the folders for public and private each time, which can simplify navigation and organization of the code. Additionally, you can use a single folder for each module or functionality, which can improve code cohesion and modularity.
The disadvantage of form A is that you lose the separation between modules or functionalities that are public and those that are private, which can make testing, security, and flexibility of the code difficult. Additionally, you can create confusion or inconsistency by mixing the concepts of domain, application, and infrastructure with public and private.

The advantage of the B form is that you maintain the separation between modules or functionalities that are public and those that are private, which can facilitate testing, security and flexibility of the code. Additionally, you can follow a consistent and uniform structure for each module or functionality (as UE5 does with a best practice principle), which can improve the readability and understandability of the code.
The disadvantage of form B is that you generate a large number of folders and subfolders (in code we can say something like boilerplate but for folders), which can make it difficult to navigate and organize the code when there are few people or lack of experience. In addition, you can cause code duplication or redundancy if you do not have experience, by having to create two folders for each module or functionality, one for the public and one for the private.

What do I think?

In general, Form A may be more suitable for small teams, working on simple, short-term projects, that do not require great scalability, reliability or security, and that do not change much. In these cases, Form A can simplify navigation and organization of the code, and avoid duplicating folders.

On the other hand, form B may be more suitable for large teams, working on large, complex and long-term projects, which require high scalability, reliability and security, and which may change frequently. In these cases, form B can facilitate testing, security and flexibility of the code, and follow a consistent and uniform structure.

However, I don’t think this means that Form A is always better for small teams, nor that Form B is always better for large teams. It will depend on many factors, experience, scalability, developers and other factors. There are cases where form A may be more convenient for large teams, or form B for small teams.

In my case, I will opt for A. Why? well… when you are small it is better to do it little by little and know your limitations.

But you can have the last choice and choose the best way that you think is convenient. If you think that putting all the actors in a folder is correct and is comfortable for you without interfaces and decoupling, that’s fine with me. How crazy am I to tell you how you have to write.

Although it wasn’t my intention. I think this could even be didactic.

The next level to this Hexagonal pattern would be to simplify it even further with generics.

“So that the services are only one.” - Breaking “directly” the SOLID principle.

Characters → GenericService <T, Request T , Result T > → GenericResponse <T, Request T , Result T > — Characters ← GenericService ← GenericResponse

Items → GenericService <T, Request T , Result T > → GenericResponse <T, Request T , Result T > — Items ← GenericService ← GenericResponse

But this would be applying an invented pattern of mine. I call it the “injective direct pattern.” I’m not very good at making up names. But hey, this would be for another publication that escapes this publication. But in summary it would be 1 single interface for everything, something like injecting anything at any time. (Sounds crazy right?)
and the next level to this generic model that I showed would be to use observers, callbacks, reflection… an endless number of things.

We did away with Public/Private folders years ago. Everything is in “Game/Source/ProjectName”.

Public/Private directories make sense for a public API project like Unreal Engine but are just needless hassle for a closed source game project.

I belive if you create a CPP project with Unreal Editor now it doesn’t give you any Public/Private folders anyway.

2 Likes

The folders are not there at the beginning, but if you add a class via editor you can select “Class Type”, default is nothing selected, then the folders are not created. If private or public is selected, these folders are created and the files in them. If private is selected, the H and CPP files are created in the private folder. If public is selected, the H file is in the public folder and the CPP file is in the private folder.

1 Like

Starting from removing the Public and private folders:
Below I present a complete example of a C++ program. But it is not necessary to do it unless it is explicitly required. I hope I have covered all the important aspects. When developing the functional and architecture, it is normal that some adjustments will be required in the future. This is designed for a specific target audience. Not for the vast majority. There is nothing obligatory in what I am commenting on. It should look like this.
Something like this. Obviously other practices can be followed.
Hexagonal + DDD + Vertical Slice. The “Screaming” would be missing, which is simply sorting subfolders by folders.

Summary

https://i.imgur.com/7lUmn1T.png

Summary

https://i.imgur.com/NmjGzMy.png

Summary

https://i.imgur.com/Z8agqxI.png
https://i.imgur.com/mOqRnKn.png
https://i.imgur.com/gVZFnfq.png

Summary

https://i.imgur.com/VZIasuv.png
https://i.imgur.com/NUpRN2f.png
https://i.imgur.com/rk1GFBm.png

What do you obtain? independent modules.
I know at first it can be a little scary for those of you who are not familiar with this type of practices.
But when you get used to it, this will be much more productive for any job. Even the Unreal Engine Internal code follows generic style practices.

Note, for the topic of Async and blueprints, there may be a thousand and one ways to do it.

But you can have the last choice and choose the best way that you think is convenient. If you think that putting all the actors in a folder is correct and is comfortable for you without interfaces and decoupling, that’s fine. If you are comfortable with this structure

Summary

https://i.imgur.com/NAShGYN.png

that’s fine. Use the structure that you think is most convenient for you.

architecture comes with the downside that rules to make it look neat come with the downside that they are actually incredibly limiting your options when the project grows.

I use a styleguide for all asset naming which is absolutely required, no way around it. You will want a short asset name which is unique. For folders, don’t add complexity.

I try to keep the folder architecture somewhat similar on all my modules but when modules grow I simply split them into separate smaller modules instead of adding complexity.

For years I have been naming my folders like the following, and since then I have experienced the pros and cons… from my own documentation:

There are three main folders in the project folder (the root where .uproject is) from which you will manage your project's content. These are:
~/Content: 
This is where the engine stores assets in the UAsset format, which are files like blueprints, textures and animations. 
~/Source: 
This is where programmers write source code, which will be mostly .h and .cpp files. No other files are stored here.
~/SourceContent: 
This is a folder I created to store files in which act like a source for UAssets like textures and animations. This is where you could store save files from external software, like Photoshop or Blender3D. This is also where you can store the output of the external software, like .fbx, .png and .wav files. During import a copy of the file will be turned into a .UAsset. By right clicking the .UAssetet in the engine you can choose to reimport a .UAsset, which will use the file from SourceContent as its source.

When a SourceContent file contains multiple exportable assets, the file name should explain what it contains in short and be suffixed with _Collection, like  this:
Source:
T_MouseButton_D_Collection.svg
Exported:
T_MouseButton_Left_D.png
T_MouseButton_Middle_D.png
T_MouseButton_Right_D.png

The key to keeping your folders managable is to re-use the same folder structure for all your  systems. This is the structure I prefer to use:
./AI/BehaviorTrees/
./AI/BlackBoards/
./Animations/Creatures/
./Animations/Objects/
./Blueprints/GameModes/
./Blueprints/Interfaces/
./Blueprints/Libraries/FunctionLibaries/
./Blueprints/Libraries/MacroLibraries/
./Blueprints/UI/
./Blueprints/World/Animations/AnimInstances/
./Blueprints/World/Animations/AnimModifiers/
./Blueprints/World/Animations/AnimNotifies/
./Blueprints/World/Components/ActorComponents/
./Blueprints/World/Components/WidgetComponents/
./Blueprints/World/Creatures/
./Blueprints/World/Objects/
./Data/Curves/
./Data/DataTables/
./Data/Enums/
./Data/Structs/
./Foliage/
./Levels/
./Localization/Text/(StringTable assets)
./Materials/FX	/Particles/
./Materials/FX	/PostProcessing/
./Materials/MaterialFunctions/
./Materials/MaterialParameterCollections/
./Materials/UI/
./Materials/World/Decals/
./Materials/World/Surfaces/
./Meshes/Creatures/
./Meshes/Objects/
./Particles/(ParticleSystem assets)
./Sounds/FX/
./Sounds/Music/
./Sounds/Voice/
./Textures/FX/Particles/
./Textures/FX/PostProcessing/
./Textures/UI/
./Textures/World/
./Textures/World/Decals/
./Textures/World/Surfaces/


Keep it clean. Split up your main folders:
~/Content/_Core/
Files in here are usually perfected code and editor assets. These files are re-usable assets and are the framework for this and any project. This folder should be written to with care and not by just anyone.
~/Content/_Developer
Assets which are not properly tested or unfinished, used to prototype new ideas, which are  experimental. It exists to not bloat any other folder.
~/Content/_Placeholders
Files that are certainly not going to be in the final product. These are required when the code needs valid assets to work which don't have yet, like icons and background music.
~/Content/
Files that can be considered only relevant to this specific project and which don't fit in the _Core, _Developer or _Placeholder folders.

Underscores are rarely added to folder names simply to keep them on top when sorting folders by name.

In my documentation you can see I also split from “world” things from UI, just adding another layer of folders to blueprints, textures, sounds. The deeper it goes the more it backfires. At that point I ask myself if I can put it into a separate module and simplify. By this rule 99.9% of the time I think of building a subsystem I actually think of just creating a new plugin instead of pushing it down into the folder architecture. I think a folder path should be 3 maybe 4 folders deep max, so you can (purely for your sanity) at least keep sounds separated from images without getting into the details.

As you can see already the assumptions are made that blueprints and meshes will contain “objects” or “creatures”. Well… If we really want to enforce a structure but don’t know what the project is going to be, we could just use the same structure as class inheritance right? Yes but it just adds complexity. You will easily spot what goes where if the folder structure looks the same on every project but apart from that asset files have a limited directory path size you are also adding to the amount of maintenance required to keep such folders “structured” by your own rules.

It’s easier to make small mostly independent modules (plugins) with their own super short folder paths, where each module has a single task (control game time, provide movement behaviors, play music). The smaller it gets the easier the folder architecture will be on the eye and the easier it is to scan folders for content by keywords.
Instead of going down the whole tree of “UObject > Subsystem > GameInstanceSubsystem > USomeCustomSubsystem” for a module which only holds a single subsystem asset and maybe just few more files.

About the private / public folder naming, I personally start with the source folder splitting into private / public immediately, keeping all .cpp and .h files separate. To me it’s easier on the eyes when navigating files manually. There’s not really a best or worst way to do it, in the end we mostly navigate by search and filter so we might not even see this structure.

In text scanning tools like Agent Ransack which I use most of the time to navigate I can simply swap file types from cpp to h. With binary files it’s either by file name or asset details. Sometimes assets like animations are reused or don’t fit in a specific folder like “human” because the animation can be played on orcs, goblins and humans, then it feels tempting to further structure the folders just to make that fit as “/creatures/humanoid/human” and place the generic animation under humanoid. It’s nothing but a trap. Simple == better.

Because asset relations are too complex to display just as a folder list it’s better to think of other ways like visualizing references, dependencies, asset details. Often asset references can also be organized through datatables with the bonus that they can be modified in excel. So many options we have without even looking at the folders.

Just an example

It’s also worth saying that anything not c++, like project settings / soft pointers / UAssets absolutely hate it when things move around. Restructuring is without a doubt going to break your settings, soft pointers, pointers to events and sounds in animations. Some things can be redirected, some things not.

We have some differences between us, both in the architecture and in the naming of variables (regardless of the forms of decoupling). This makes me think of the debate there was about which pattern to choose for Java Spring Boot and what type of architecture to use. The community opted for Hexagonal, because it seemed to meet all the requirements.

some differences between us

For example, in the name of the classes with the standard. I use camelCase, instead of snake_case or kebab-case. I guess it’s preferences. For example, Unreal Engine follows the PascalCase standard. My experience with kebab-case and snake_case with the “-” and “_” sign has caused me some problems, as silly as they may seem.

camelCase and PascalCase are too similar except for the initial letter since one is uppercase and the other is lowercase. In general, it can be said that the camelCase is used more for naming variables, functions and methods, while the PascalCase is used more for naming classes, interfaces and namespaces. While for database snake_case or camelCase. I see that you use a mix of snake_case and PascalCase. I don’t agree with your way of decoupling the domain.

On the one hand, you seem to value simplicity, modularity and reuse of resources, which is good and in line with hexagonal practices. On the other hand, it seems that you do not follow a clear separation of layers, responsibilities and interfaces, which can hinder the maintenance, extensibility and adaptability of the system.

I could ask you all the questions I have based on what you mentioned, but that would make for a post that is too long and would escape this one about your way of doing it. The same would happen in each project and person. Epic appears to follow “a clean code principle” (vertical slice).

Are good practices recommended? From my experience I can say yes, but not for all cases. Really apply good practices thinking you are doing good practices? not really. It is a set approved by many. (We look at the best one among all and start from that. I like to apply good practices? It is too complex to answer this for me.

Well… if I am alone, I would not apply good practices, but I really have to ask myself if it is my project? Am I going to work with a team? Do I want it to be possible to scale? Now, if I am working with a company and there are different people on a project (let’s say 20 people), it is better to have it separate. Both for scalability and decoupling and maintainability. It’s like following the same practices as EPIC with its Unreal Engine project, but adding value.

In 2014-16-18 when I entered the Unreal Engine world (since in 2014 they made it free) I came from CE3. Although I had to leave Unreal Engine a couple of times for a number of reasons. So when I more or less returned in mid-2023, I remembered the 2016 post I made. In 2016 I put up the post “scalability of system” (post that, by the way, was deleted since people did not understand anything I said. I don’t know if it was lack of experience or that, but only nonsensical answers came out), in which I asked for ways to have scalable code. Nothing was achieved in that post. It is normal.

I recognize that each software architecture has its own complexity and that it is not always easy to understand it. There are different ways to apply good practices, and there is no obligation to follow them, but good practices help the programmer or programmers.

I always say the same thing; “If everything fits in a folder and works perfectly, I have no problem.” But if you ask me for help, maybe I won’t be able to offer it to you, not because I can’t or don’t want to, far from it, but because I’m not familiar with your way of organizing the code and the folders or what they call each other, so I have to learn n ways of doing things. There are too many projects. I think that in that case, it is better to follow one type of architecture.
I prefer to work on projects where the code is well structured, modularized and hey, if it is documented, even better, because this way I can add more value and avoid errors.
I’ve seen projects where everything was in a single file with thousands of lines of code, and it was a nightmare to edit, debug, and maintain. A bad commit, whether by n programmers or a poorly moved package, could ruin the entire project. I know that there are people who think that organizing the code by folders is enough, and that it is easy for them to work like this. But I believe that that is not enough, and that other techniques must be used to organize the code, such as dependencies, injections, generics and other elements that allow the layers to be decoupled and facilitate teamwork. That is what is done in business projects, where certain requirements and quality standards must be met.

If I see a project that does not meet those requirements and standards, I prefer not to participate in it, and I would even leave if there is no other option.
I don’t want to be fired because of a complex guy who came up with so-and-so in his time.

Now if the project is small and there are few people working on it, it may not be necessary to use a very elaborate architecture. The architecture is not there to complicate the code, but to help the programmer who comes after you, or in other words, to help your successor. I don’t want to seem like the typical one who says “do this like this”, far from it. Do it as you need or want. That’s why I asked EPIC directly how they handled the issue and @Shmoopy1701 answered exactly what I needed. Thanks to the elimination of public and private folders it has resulted in the correct structure for Hexagonal + DDD + VL + Screaming.
I will follow this architecture, let’s say it would be a step further than what EPIC does.


But you really have to think about whether or not it is worth implementing such an architecture. Maybe not for a small project.
More info on the architectures. Info

1 Like

There are no “good” practices. There are those that are suitable and convenient for you PERSONALLY. If you don’t create an MMO, then you don’t need to “scale” or “maintain” the game like any other software. More than half of the tips for “clean code” are meaningless and sometimes even harmful for game development. Architecture is not about theory, architecture is about practice. You WILL have to code it unclearly, and then understand how to put it together, if you are not a systems architect with 10 years of experience. The only thing I can recommend is to look at how the code of really good programmers is organized. On Github there are the entire games Half Life 1, HL2 Deathmatch, and also Quake and Doom 3. Many of them do not have any division into different folders at all, all classes are together, sometimes even with headers, with the exception of individual things. And you can surprisingly find it convenient. Even though it sounds counterintuitive.

p.s. I saw projects in which everything was in one file, with thousands of lines of code and editing, debugging and maintaining, was HEAVEN. Because it was written by a good programmer. So I repeat, the same method can be used in different ways by a good specialist.

I saw the last comment and I was encouraged to do it.

Now that I realize and because of what is appreciated, the post became didactic. What was a question, became something more.
I could take it to the next level.
It could serve to collect from “the whole” community the best forms of architecture for Unreal Engine that are used by all users by sharing knowledge with each other, so as to improve either for us or for the following. What might work for some practices does not necessarily work for others.
For example, with an example of the class diagram and some code.

In my case, I found something different since UE5 doesn’t have an official EPIC framework for C++ (I’m sure there is one) for the database. Therefore, you have to do it manually with DB or APIS drivers.

I found some general differences of doing it with C++ and UE5.
I publish something I did: Hexagonal + vertical slices.

I have published it on an old wordpress page that I have (which is under construction)

(it may change slightly over time, but this should be the structure)
Note: I have tried to follow the best possible practices. As I don’t have a generic framework (The problem of not being able to use generics is that we will have to “fix them”) I made them by hand. If there is some purist who does not agree with some point, I am all ears to improve.

Summary

Note: I choose Character base since the Engine project already has the character. 10>UCharacter.h(12): Error : Class ‘UCharacter’ shares engine name ‘Character’ with class ‘ACharacter’ in E:\UnrealEngine\Engine\Source\Runtime\Engine\Classes\GameFramework\Character.h(231)

Note: For the theme of constructors, UE5 is a bit special and there are things that cannot be done.
Note: For the theme of generics, uff, Unreal Engine does not support having inheritance of generic classes for themes that need to know all the classes before execution. That’s why you can’t.

What do we get with this?
1 - Decoupling (What does this mean?)
Well, let’s say that you can “delete” the last ports (access between layers) that does not affect the initial ones. For example, Infrastructure, we can change the database or even remove it and have it locally.
2 - Reuse when inheriting Character (For the Player controller and secondary classes). The Character class will no longer have to be modified and you can add N Characters. (I thought of doing it with Actors, but then I would have to rely too much on generics, which makes the code difficult)
3 - For unit tests you only test UCharacterService and use its methods.

Note: No, chat gtp 4 does not know how to do this even remotely. That’s why I upload the example. For some reason I don’t see any tutorials about this either.
Note: Without Rider it would have cost me more.

With this, we can create n characters of character type Player, NPC, IA, Enemy, Mobile cars, … and all character extenders without having to program more the in/out interface layer to store or retrieve to/from the DB any type of character.

Do it 1 time.

It will also help me to have you here on the forum sharing opinions.

We can make this POST something educational and the community can publish over time the best practices of each team on its structure and thus learn from the whole world for the purpose of teaching. Me the first

“There are no “good” practices”, “There are those that are suitable and convenient for you PERSONALLY”, “If you don’t create an MMO,”
" I saw projects in which everything was in one file, with thousands of lines of code and editing, debugging and maintaining, was HEAVEN. Because it was written by a good programmer. So I repeat, the same method can be used in different ways by a good specialist." …

I have no words nor do I want to have them. Learning to program UE5 as a hobby is not the same as being trained and working in a AAA company or working in EPIC on the Unreal Engine project. Internet being Internet…
I imagine going to work for EPIC on the Unreal Engine project and saying what you said… “now I understand those 300gb of the project”. Anyway

The purpose of this post is not to enter into a debate about whether it is good or bad, because that makes no sense. The purpose is that people can publish their best practices. Let’s hope that the Internet is not the Internet again, but that’s a dream.

I encourage and urge those who consider that they have good practices to share it with the community in order to grow in knowledge.
Thanks in advance to everyone.

Note: To the EPIC development team: It’s a bit annoying to work with objects by forcing them to behave like generics. Hopefully you find a way to solve it; otherwise you’ll have to use a combination of reflections and objects.

1 Like

The purpose of this post is not to enter into a debate about whether it is good or bad, because that makes no sense.

That is why I suggested looking towards Doom, Quake and Half Life. Tons of good practices that I use. But speaking in specifics, this requires a little more time. Knowing how attached people are to OOP in our time, I suggested recalling other methods and extract the best practices from them.

I have updated the practices.

Before:

  • Return: The return is in layers since it is the standard procedure.
  • ID: Integer

Now:

  • I remove the return. Now it goes by reference now (Personal preference. It can be done with the return if you want). I have found it better in this case, for the async issue and others instead of using a TFuture and promises.
  • Id = Object. I didn’t realize that MongoDB uses a Key JSON. I also didn’t think of a filter composed of two or more fields. For that reason I put it Object. We can have a DTO, but I don’t see it as necessary.
  • cons: It is respected that the object cannot be modified. (Although it does not make much sense since what is going to be modified is the ref and not the obj, but it is put anyway, so that it only has one purpose, that of passing a type of object reference and that the object itself cannot be modified)

Purpose:
In order to make requests independently of a return so as not to have to use TFuture and promises between layers. These practices are even a little more in line with those of Unreal Engine.

Note: I don’t know whether to give a Mysql and MongoDB example for the CRUD (Driver/API).

IIRC UBT doesn’t recognize public and private folders unless they are at the root. So having a Character folder, then having public and private folders does nothing. Public and Private have to be at the top.

It seems to me most people are transitioning away from public and private folders altogether.

Afaik that is what you configure in the build.cs file by changing ******IncludePaths. Don’t remember exactly why I put this on my project, think it was necessary to have any folder structure at all, at least maybe 10 years ago :') it works for me and I kept it in. :

using UnrealBuildTool;
using System.IO;

public class SomeProject : ModuleRules
{
	public SomeProject (ReadOnlyTargetRules Target) : base(Target) {
		IncludeSubDirectoriesRecursive(ModuleDirectory + "\\Private", true);
		IncludeSubDirectoriesRecursive(ModuleDirectory + "\\Public", false);
 
		PrivateDependencyModuleNames.AddRange(new string[] {

		});

		PublicDependencyModuleNames.AddRange(new string[] {
			"Core" 
			, "CoreUObject"
			, "Engine"
			, "InputCore"
		});
	}

	private void IncludeSubDirectoriesRecursive(string DirectoryPathToSearch, bool bIsPrivate) {
		foreach (string DirectoryPath in Directory.GetDirectories(DirectoryPathToSearch)) {
			if (bIsPrivate) {
				PrivateIncludePaths.Add(DirectoryPath);
			}
			else {
				PublicIncludePaths.Add(DirectoryPath);
			}
			IncludeSubDirectoriesRecursive(DirectoryPath, bIsPrivate);
		}
	}
}

I do separate private and public at the root because the .cpp and .h files have such different purposes the structure is cleaner for me. I still don’t visualize the files differently from their actual directory structure :slight_smile: .

1 Like

Hello @Drakgoku !

I’m diving into UE5 and I’ve been reading the information on SOLID, CleanCode and the hexagonal architecture that I use in my daily life as a developer. I work with Java, Typescript (and various back/front frameworks).

Looking at the documentation I wondered on a C++ project which architecture to start with.
I carefully read the twists and turns of the post; and I understand very well where you end up; but as much as this architecture works very well in a web/industrial context, I can’t imagine how you are going to use it in a “closed” video game; unless your project is that of an MMO or a multiplayer game which requires DB/API/other access in the background; because I don’t understand the point of implementing all the repositories/services (CRUD) because in the end, apart from the save/load, the game keeps its state as long as it is launched. Which makes me a little doubtful about the relevance of the hexagonal architecture applied as it is.

What are the constraints of your project that made you decide that this architecture was the right one in a video game context? Is it still the same 4months laters ?
Thankyou :slight_smile:

Is it still the same 4 months later?

Of course. The code is for life. “Do it once.”

What does hexagonal architecture allow you?

1 - Decoupling: Allows you to separate the core of the business logic from the infrastructure and presentation layers. This is especially useful in video games where you can have multiple systems interacting (physics, rendering, AI, Multiplayer and others), facilitating the independent management and evolution of each one. Especially on large projects.

2 - Flexibility: By defining clear interfaces between the core and adapters, you can easily change infrastructure technologies without affecting the core logic of the game. Facilitating individually specialized work

3 - Testability: Improves the ability to test business logic in isolation, without depending on external elements such as databases or APIs. This can be crucial to ensure the quality and stability of the game.

4 - Scalability: The hexagonal architecture allows specific parts of the system to be scaled independently, which can be beneficial for games that may require performance improvements in specific areas.

To do this you need OOP knowledge, to be clear about interfaces, structures, polymorphism, generics, type conversion… the basics. Then understand the pattern of the acronym SOLID and others, finally Hexagonal.

When all the possible ways to learn Hexagonal are met, you will see that the project is structured in a business way. That is, prepared correctly for a team of 10 or more people.

In short, have a modular project lifecycle, where each module can be removed without affecting other parts of the code and each developer only works on the relevant module.

This also makes it easier when the engine is updated since the interfaces will never change only the implementation. I know, amazing.

I always say the same thing. If there are only a few of you doing something Indie you may not need it.

If you are a large team where the most important thing is to have the game updated to the latest version, I highly recommend it.
Is it more work? of course. If it were easy everyone would have the game updated to the latest version of the engine. 90% of companies take too long to update a simple version. Why? Too much non-modular code. For example, the lyra project is a clear example of what not to do. But everyone says it’s good to learn… anyway.

Is Lyra project setup WRONG? - Development / Programming & Scripting - Epic Developer Community Forums (unrealengine.com)

That is the most optimal situation for all your custom code. However, since code usually depends on engine code (which is a bug dump) every change in an engine version still requires testing of your own code as a whole… and the latest versions (especially UE5) seem to be avoided for exactly this reason.

I would totally pick 1 engine version and stick with it if EPIC provided bugfixes on one, but they don’t. On a large scale project with XXX amount of people working on it I can’t imagine keeping that on the latest versions.

The latest horror show I wrote a custom implementation for is the character / pawn movement component / pawn movement implementation. Once you’ve seen that you’ve seen it all.

Leaving this link here :slight_smile: I really do hope this will improve.

Request for LTS versions of UE and marketplace assets.

Last time I didn’t notice, but our gitignore approach is different (blacklist vs whitelist).
For funs I post mine.

The folders “_Documentation, SourceContent, _Concepts” are not usually present, and it’s debatable if they should be in the repo at all. It’s useful on smaller projects.

# First, ignore everything
*
# Now, whitelist anything that's a directory
!*/

# base files
!**/*.uproject
!**/*.uplugin
!**/.gitignore

# _Documentation, whitelist everything
!**/_Documentation/**
# Ignore OpenOffice temp files
**/_Documentation/**/.~lock.*

# Config
!Config/**
!Plugins/*/Config/**

# Content
!Content/**
!Plugins/*/Content/**
# Content Built data for maps
**/*_BuiltData.uasset

# Resources
!Plugins/*/Resources/**

# Source
!Source/**
!Plugins/*/Source/**

# Script
!Script/**
!Plugins/*/Script/**

# Configuration files generated by the Editor
!Saved/Config/Windows/*.ini
## Config consists mostly of shared files except per-user editor settings
# Saved/Config/**/DefaultEditorPerProjectUserSettings.ini

# Whitelist PakBlacklist-<BuildConfiguration>.txt files
!Build/*/PakBlacklist*.txt
# Don't ignore icon files in Build
!Build/**/*.ico

# Cache files for the editor to use

# Art / concepts

# SourceContent, whitelist source files other than code.
!SourceContent/**
!Plugins/*/SourceContent/**
# blacklist temporary or junk files.
**/*.blend1


# Concepts (Stories / Art), whitelist everything
!SourceContent/_Concepts/**
!Plugins/*/SourceContent/_Concepts/**


When possible I prefer to blacklist everything by default, then whitelist just what I need. It doesn’t per se result in less need for management but it does make sure no junk enters the repo.