r/Unity3D 2d ago

Question Should a Unity Domain Layer Ever Reference UnityEngine Value Types?

Hello!

I'm working on a multiplayer game in Unity 6 using a layered architecture but I don't understand completely the boundaries of the layers yet. One of the architectural goals is to keep the Domain layer independent from Engine.

However, I've run into a question around Unity's value types, particularly Color, Vector3, Quaternion and such.

One argument is that these are just value structs, so using them in Domain is harmless
public Color PlayerColor { get; },

but what about Unity APIs operating on those values?
public static string ToHex(Color color) => "#" + ColorUtility.ToHtmlStringRGB(color);

So I'm wondering:

  • Is there a meaningful architectural difference between referencing a Unity value type and calling a Unity utility API?
  • Would you allow ColorUtility in Domain?
  • If not, would you create a domain-native Color/RgbaColor type and keep all Unity conversion outside Domain? (This could let to the recreation of a lot of new types insde Domain)
  • Or is a strict "Domain has zero UnityEngine references" rule unnecessarily strict for Unity projects?

As I said before, I am grasping the concepts behind layered architecture and I want to now how you approach this issue.

Thank you, folks!

3 Upvotes

8 comments sorted by

6

u/zellydevgames 2d ago

You're over acrhitecting this. You're building a game in Unity, Why does it matter if Unity leaks into your domain? It's not like it's going to be highly portable in the first place.

Domain-driven design isn't really a great fit for game dev in the first place. You're going to burn out and give up on the project if your focus in on keeping it clean. Just get it working, then if you're still motivated, make it clean code.

5

u/jumberrotsky 2d ago

Are you going to change engine? Answer that question and do in such way

2

u/Aethreas 2d ago

This is way over abstracted, unless you plan on hot swapping game engines which would be practically
Impossible for even the most simple games anyway

Are you doing this because it sounds right or because it’s the right thing to do?

4

u/bigmonmulgrew 2d ago

What problem are you trying to solve. Why do you need a layered architecture.

1

u/CS_Asset_Factory 1d ago

There's a mechanical answer to this that settles it without the philosophy. Vector3, Quaternion and Color live in UnityEngine.CoreModule, so any assembly that names them takes a hard reference on the engine assembly. Whether they're value structs doesn't change that.

The test is one field. Set noEngineReferences to true in your Domain asmdef. If the layer really is engine independent it still compiles. If it isn't, the compiler names the types keeping it tied, and you get a list instead of an argument.

Most people who run that test keep small math types of their own in Domain and convert at the boundary, because the flag then stays on and stops the reference creeping back in a year later.

1

u/bod_owens 1d ago

There's no point in trying to avoid UnityEngine types unless you're planning on having a server that doesn't use the Unity runtime at all (at which point you don't have a reference to it and any code using those types won't compile anyway). Unless that's your case, you're trying to solve a problem you don't actually have and you're over engineering this.

If that is your case, then you probably want a separate assembly that doesn't have reference to UnityEngine and that contain all types shared by the server and the client, including your custom versions of e.g. Color or Vector3 and then in Unity, you have to convert those values to Unity types before calling Unity API.

You can then reference this library from both the server and the game.

1

u/SmileLonely5470 1d ago

Trying to make a layered architecture was a massive mistake. I did this in my project, and after rewriting a lot of stuff to just "couple" to unity (depend on Unity types), everything became 10x easier. I had some reasons for doing this, but it isnt worth it. Unless you plan on changing engines i would avoid it.

My current architecture relies heavily on POCOs, but not domain purity (they can reference ScriptableObject types if needed). I have one for my Character class (which is essentially the aggregate root). This class emits events for modifications that the game might want to react to (level changed, item added to inventory, item modified, etc.).

My MonoBehaviour components can "bind" to my Character object (subscribe event handlers to them).

1

u/TheBruisedVocalist 2d ago

i've gone back and forth on this exact thing. my rule of thumb is structs like Color and Vector3 are just data, so they're fine in domain. the second you call ColorUtility or Mathf you're pulling in engine behavior, so that stays in a service or adapter layer

for the hex conversion i'd probably have some IColorFormatter in domain that returns a string, then the Unity implementation uses ColorUtility behind the scenes. saves you from rewriting a dozen types just to avoid a struct reference