This is Fine Gopher by MariaLetta

Refactoring a PHP app in Go: Entity and Value Object

by

In this blog, I have posted a series of articles on how to create an app using Clean Architecture and DDD, albeit a simplified version. Now, it's time to bring these concepts to Go.

Golang is considered a "light" object-oriented language since it supports concepts like encapsulation, polymorphism, and interfaces, but it doesn't use classes; it uses structs, and it doesn't have inheritance; instead, it has struct embedding.

Refactoring an app isn't an easy task, even with the help of ChatGPT, especially when you need to shift paradigms and concepts due to the new language. What was considered good practice in the original language could be considered bad practice in the new language.

In this article, I will show a refactor of an entity and a value object originally created in PHP that were converted to Go. Then, I will explain the changes in the code structure.

Entity

Take a look at this entity called BackupKeys in PHP:

There is nothing special about it. It holds the key pairs of an Iam-based authentication which are value objects, it has a jsonSerialize method and we have a couple OpenApi annotations.

Now, let's take a look on the same entity written in Go:

OMG! He's using panic in Go, call the police.

You might be surprised to see that I'm using panic in Go, but coming from a cybersecurity background, every single input and output provided by the user or the infrastructure gets converted into value objects so that I never use "impure" data within my application. This approach makes it difficult for attackers to exploit applications.

Keep in mind that I use an ErrorHandlerMiddleware to catch exceptions and try/catch on for loops to skip iterations that won't create instances of my objects correctly when possible.

Back to the code, you'll see that my class is now a struct. The __construct method is now a method called NewBackupKeys, and it's separate from the struct itself. Since it's within the same package, I can call this method as long as I'm importing the entity package.

Since the constructor is not inside the "struct scope", I need to be very explicitly on its name. This does not happen with the GetAccessKeyId and GetSecretAccessKey methods because there I'm using (self BackupKeys) before the name of the method, therefore Go understands this "as if" the method is part of the BackupKeys struct.

Methods inside structs that are actually outside, you said what?

Methods inside structs that are actually outside are a bit tricky. We don't use the (self BackupKeys) on the method NewBackupKeys because the struct wasn't created yet at this point, which is a chicken and egg situation.

The self keyword isn't a reserved keyword in Go, but I got used to this keyword due to Rust and I kept using it when referring to the struct inside the file I'm working on. Beware of that when dealing with multiple structs on the same file, though.

Instead of jsonSerialize we have a MarshalJSON method, and it also has the (self BackupKeys) declaration, so we can call it a generic name.

Another important thing to notice is that when you use a capitalized property name, you're declaring it as public. Since I prefer to use getters instead of using the property name directly, you'll see that both properties of my struct start with a lowercase character on purpose.

Value Objects

Now, let's take a look on the Value Object named AccessKeyId in PHP:

Very similar to the entity before, but in this case, we have a validation method throwing an exception on failure. In Go, we'll use the panic method:

You'll also notice that there is a __toString version called simply by String. Since it'll receive the struct before the actual name of the method, it can also have a generic name.

The last thing you see is the isValidAccessKeyId method. Just like the constructor-like method NewAccessKeyId which will not be receiving the (self *AccessKeyId) statement before the method name (as the struct is not ready yet at this point), it must have an unique name.

Conclusion

Go is fast and one of the easiest compiled languages to learn. It not only has the friendliest mascot but also a vivid and mature community and core lib. I hope this article sheds some light on how to learn Go when coming from an OOP language such as PHP.

Stay tuned for the next articles. Soon, I will be showing how to convert the Presentation and Infrastructure layer from PHP to Go. See you next time!

Comments