Refactoring a PHP app in Go: Entity and Value Object
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:
You can 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.
The constructor is not exactly inside the "struct scope", thus I need to be very explicitly on its name. This does not happen with the GetAccessKeyId and GetSecretAccessKey methods because there I'm using (bk BackupKeys) before the name of the method, so Go understands this "as if" the method is part of the BackupKeys struct.
Methods inside structs that are actually outside are a bit tricky. We don't use the (bk BackupKeys) on the method NewBackupKeys because the struct wasn't created yet at this point.
Instead of jsonSerialize we could have a MarshalJSON method if we want to encode the struct in a specific fashion, but since both my props there are simple custom strings, the `json:"name"` notation is enough to tell Go how to properly handle the json encoding of my struct.
Another important thing to notice is that when you use a capitalized property name, you're declaring it as public.
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 have two possible constructors:
Handle error gracefully in Go is possible thanks to the double return "(type, error)". However, I also want to have the option to stop the application without having to use "if err != nil" all the time, such as on controllers. Be careful when using panic though, make sure you have a middleware to capture the stack trace and sanitize the output correctly.
You'll also notice that there is a __toString is now just called String() and the isValid method is pretty much the same thing.
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