Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

From Discord: Convenience Methods for Service Function Access #205

Open
effect-bot opened this issue Jul 18, 2023 · 0 comments
Open

From Discord: Convenience Methods for Service Function Access #205

effect-bot opened this issue Jul 18, 2023 · 0 comments

Comments

@effect-bot
Copy link

Summary

The discussion revolved around the use of convenience methods to access service functions. While it may seem convenient to have top-level functions that directly access service functions, it can lead to anti-patterns and make it easier to leak dependencies into service interfaces. This can make it harder to manage dependencies and eliminate them when using the service elsewhere.

Key takeaways:

  1. Convenience methods to access service functions can lead to anti-patterns.
  2. Dependencies should be lifted into service construction as much as possible.
  3. Avoid leaking dependencies into service interfaces.
  4. Use layers to properly manage dependencies.
  5. Be mindful of the potential foot-guns and consider the long-term implications of convenience methods.

Example article

Convenience Methods for Service Function Access

When working with services in the Effect-TS ecosystem, it's common to have functions that provide convenient access to specific service functions. These convenience methods can make it easier to use the service functions in your code. However, it's important to consider whether this pattern is appropriate for your specific use case.

The Pattern

The pattern involves defining helper functions that wrap the service functions and provide a more convenient interface. Here's an example:

interface SomeService {
   readonly getString: Effect.Effect<never, never, string>;
   readonly getNumber: Effetc.Effect<never, never, number>;
}

export const SomeService = Context.Tag<SomeService>();

export const getString: Effect.Effect<SomeService, never, string> =
    pipe(SomeService, Effect.flatMap(ss => ss.getString));

export const getNumber: Effect.Effect<SomeService, never, number> =
    pipe(SomeService, Effect.flatMap(ss => ss.getNumber));

In this example, we define getString and getNumber as convenience methods that wrap the corresponding service functions. These convenience methods can then be used in other parts of the code to access the service functions more easily.

Pros and Cons

There are pros and cons to using this pattern, and it's important to consider them before deciding whether to use it in your code.

Pros

  • Simplifies code usage: Using convenience methods can make the code that uses the service functions more concise and easier to read. Instead of having to access the service functions directly, you can use the convenience methods with a single line of code.

  • Encapsulates service access: By providing convenience methods, you encapsulate the access to the service functions within a single module or file. This can make it easier to manage dependencies and ensure that the service functions are used correctly.

Cons

  • Leads to anti-patterns: One of the main drawbacks of this pattern is that it can lead to anti-patterns. By providing convenience methods that directly access the service functions, you may be bypassing the proper construction of the service and introducing dependencies where they shouldn't be. This can make it harder to reason about the code and can lead to issues down the line.

  • Redundant code: Another drawback is that this pattern can introduce redundant code within the module or file where the convenience methods are defined. This can make the code harder to maintain and can increase the risk of introducing bugs.

Best Practices

To avoid the potential issues associated with convenience methods for service function access, it's recommended to follow these best practices:

  • Lift dependencies into service construction: Instead of accessing service functions directly within convenience methods, try to lift the dependencies into the construction of the service itself. This ensures that the dependencies are properly managed and eliminates the need for convenience methods to access the service functions.

  • Use layers for dependency management: If you find yourself needing to access service functions in multiple places, consider using layers for dependency management. Layers allow you to define the dependencies of a service at the point of construction, making it easier to manage and reuse the service in different parts of the code.

  • Avoid leaking dependencies: Be mindful of not leaking dependencies into your service interface. Make sure to properly encapsulate the dependencies within the construction of the service and avoid exposing them through convenience methods or other means.

Conclusion

Convenience methods for service function access can be a useful pattern in some cases, but it's important to consider the potential drawbacks and follow best practices to avoid issues. By lifting dependencies into service construction and using layers for dependency management, you can ensure that your code is more maintainable and less prone to bugs.

Remember to always evaluate the specific requirements of your project and choose the approach that best fits your needs.

Discord thread

https://discord.com/channels/795981131316985866/1130995105579417630

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant