Reusing Code
There are three ways to share code between distinct components: APIs, shared libraries and copy/paste of source code. All three have their valid use cases.
APIs, i.e. a server that exposes well defined functions to clients is the architecturally most robust option because client and server are completely decoupled. As long as the interface does not change, client and server can be developed independently. It is also the most expensive solution: usually and the shared code typically needs to be deployed on separate infrastructure and solutions for handling failures and outages have to be found.
In cases where the effort to build a separate API is not justified, you can use a shared library. Client code and shared code now are tightly coupled. Changes in the shared code require building and redeploying the client code too. That is acceptable in some cases, painful in others.
Shared libraries come with a downside. If there are multiple components are dependent on a library and that library change, you will need to rebuild and deploy all dependent components. Alternatively you can juggle with different versions of the shared library, which needs to be carefully controlled to avoid forking your shared code into multiple, potentially incompatible versions.
If that is too much of a problem, then it is better to copy and paste code between the components. That is perfectly acceptable in cases where the code does not contain business logic. For example a database access layer is defined by the database structure. It is a little more effort to maintain it independently in multiple code bases, but you will get it right for every component that accesses the same database. In particular for accessing data in a common database this can lead to better code: a generic, shared database layer often loads too much data for a specific use case, e.g. loading an entire object graph even if you need only the value of a single field.
Copying and pasting code does not work for business logic that has to be identical in all places where it is used. It has to be coded and verified once. Best to put it on a server behind an API, but if that is not feasible, a shared library is necessary and you have to live with the downsides of tight coupling.
Either way, “don’t repeat yourself” should not be seen as a dogma but carefully reviewed and applied (or not applied) as it fits the use case at hand.