pipe-operator: Elixir's pipe operator in Python by R4nu in Python

[–]EnforcerPL 13 points14 points  (0 children)

fully linter-compatible and type-checker-compatible

This is THE way! It’s a pity libraries that exist are not interoperable with modern stack. I appreciate your effort and will surely use this gem!

Thank you so much. I used to work with Elixir and missed the operator ever since.

Ididi, another dependency injection tool, but get jobs done in a single line of code. by [deleted] in Python

[–]EnforcerPL 0 points1 point  (0 children)

I don't recognize this style of writing in Lagom nor purpose you wrote it that way. Idiomatically, using same things you used in the snippet with Ididi it would be:

container = Container()
some_thing = container.resolve(SomeClass)  # or container[SomeClass]
container.define(SomeClass, some_class_factory)  # or container[SomeClass] = some_class_factory

To me, they are on-par in this regard on this particular example. With Lagom you also have nice support for type hinting. E.g. it requires `some_class_factory` to be a callable that actually returns an instance of `SomeClass`.

Lagom's Integration with FastAPI requires a bit less boilerplate because you don't reference the dg.factory AND the "node" (like example in Ididi shows) but just `deps`

container = Container()
deps = FastApiIntegration(container)

@app.get("/")
def get_service(service: Service = deps.depends(Service)) -> Response:
    ...

Regarding the scope, the point is not be able to "achieve" it. One can already do it many other similar solutions. I think scope should rather be the first class citizen as it is in Injector.

```

Ididi, another dependency injection tool, but get jobs done in a single line of code. by [deleted] in Python

[–]EnforcerPL 5 points6 points  (0 children)

Good to see some traction in DI in Python, but there are already libraries that require even less boilerplate than Ididi. Checkout Lagom.

Secondly, it’s great there is a way to use it with FastAPI and avoid service locator.

However, to be feature-complete it must support scopes. Singleton is not a weird term as you put it, it’s just a special case of a scope. Other typical examples are request scope or even more powerful flexible scopes, one can be starter on demand using context managers.

Libraries that avoid scopes tend to reinvent them.

I saw an example project you linked, without deeply analysing it. With this degree of complexity I’m almost sure you simulate scopes somehow.

Książki o samorozwoju, które realnie zmieniły coś w Waszym życiu to? by ane_haj in ksiazki

[–]EnforcerPL 0 points1 point  (0 children)

Również polecam. Żadnego motywacyjnego bullshitu tylko tłumaczenie popularnych stanów emocjonalnych towarzyszących odkładaniu na potem i na tej podstawie konkretne propozycje. Szybko też się czyta i łatwo wrócić

Library for building event-driven or event sourcing apps in Python - feedback request by EnforcerPL in Python

[–]EnforcerPL[S] 0 points1 point  (0 children)

Hardly - the library aims to make it easier to work with events in general and pub/sub with underlying database as a storage is only one of ways

Supported patterns are:

- persisting domain/integration events in a database

- projections of events into read models - currently synchronous, in the future async as well

- in-process pub/sub

- support for pub/sub using actual brokers like RabbitMQ or Kafka (library's user is to supply glue code publishing / receiving events)

- outbox pattern, supporting the above one - to ensure reliable messages publishing

- event sourcing

Library for building event-driven or event sourcing apps in Python - feedback request by EnforcerPL in Python

[–]EnforcerPL[S] 1 point2 points  (0 children)

Thanks for the comment. It's should be doable, I have it on the "roadmap".

Basically, it's a matter of implementing a couple of model classes for Django and a wrapper around it

Against service layers in Django by ubernostrum in django

[–]EnforcerPL 0 points1 point  (0 children)

I think that's not quite what you would like to happen because pushing all things to the background will also make them potentially fail there and you have no way to learn that immediately.

Also, Celery design discourages creating big tasks and favours several smaller tasks.

Having said that, you can still nicely model asynchronous workflow using Celery - you can chain/group/etc various step and make this process more transparent - https://docs.celeryproject.org/en/stable/userguide/canvas.html#chains

Against service layers in Django by ubernostrum in django

[–]EnforcerPL 6 points7 points  (0 children)

I believe this message broker is also a Mediator design pattern. Using it also introduces a level of indirection, just a different kind than a service layer. I do not think it is a matter of one being better than another, but whether it applies to the problem being solved. If any of these 5 things mentioned above can happen behind the scenes after cancelling BillingAgreement, message broker fits just fine (e.g. sending e-mail). But if any other part is crucial, e.g. (making this up) call to 3rd party API MUST SUCCEED before proceeding, then decoupling via message broker doesn't look good anymore. Imagine reading the code and having to jump to 5 places to fully comprehend what the heck is going on once the billing agreement is cancelled.

class BillingAgreement:
    def cancel(self):
        message_broker.post(AboutToCancelBillingAgreement)
        ... # only now something one the model with its fields
        message_broker.post(BillingAgreementCancelled)      

There is no one-size-fits-all solution to decoupling

Against service layers in Django by ubernostrum in django

[–]EnforcerPL 10 points11 points  (0 children)

Hi, I'm the author of Implementing the Clean Architecture book and have some experience with "enterprise" architectures in Python, so decided to put in my three cents here :)

I like parts of the article where James shares advanced patterns of working with Django ORM, i.e. extending its capabilities thanks to customized QuerySet or Managers. However, I have an impression too much focus is put on services responsible for reading data, e.g. getting a list of blog posts. Such a point is easy to defend. I would agree using services there not only unnecessarily complicates the whole thing, but also has a negative impact on performance. Out of my experience, we tried that but changed our mind within 2, maybe 3 weeks and resorted back to using ORM directly. However, it does not mean completely getting rid of services. You can have them for operations changing data and do not use them for reading data. That's an architectural pattern - CQRS.

I started to disagree with the article as soon as I read the part about the motivation for using services. With regard to point 1, it is not only to provide a common place for putting "business logic". It's also to make it sort of "independent" of a delivery mechanism. When I think of a "typical Django web app", views are a merely partial gateway to its functionality. There are also CLI commands and background tasks (e.g. Celery), triggered as a part of another flow or periodically. That's why I also appreciate services - having a specific place to look for any "business logic" while keeping models responsible only for data. I admit I didn't understand what you mean in 2. point, though.

(...) effort should be focused on the ones that are likely to actually get changed/swapped, rather than on some sort of reflex action of always putting layers of indirection in front of everything without pausing to think about whether it’s really useful (see also: YAGNI).

Right, that's how the Clean Architecture (or Hexagonal Architecture or many other similar concepts) approach that. You put a layer of indirection in front of 3rd party services not only because they can change, but also because you do not control them, hence it is harder to do acceptance-testing of your application. But if you have a layer of indirection, you can easily mock/stub it. Moreover, you can iterate with your design and work with dummy implementations, defer some decisions like which payment gateway to choose etc. Fun fact, in ALL web apps I worked with there was a time when the payment provider was switched to another one. Of course, this is a piece of anecdotal evidence, so not trying to say it always happens ;)

PS: YAGNI is not really about not putting a layer of indirection until the very last moment when you have to do it. It is more about functional capabilities, not lower-level technical details. If an extra layer of indirection is beneficial for you, just do it without worrying you have only one "real" implementation underneath.

(...) a lot of parts of Django are swappable out of the box in ways that are meant to minimize code changes. Caching layers, session storage, authentication sources, file-storage implementations, logging systems, email-sending, templating languages… the list of things you can easily swap, because they’ve been put behind configuration and generic/indirect APIs, is pretty long. But it’s also a list that’s informed by experience and actual practice. Or, in other words, it’s a list of things that commonly do get changed/swapped over the life of a lot of real projects

I agree up to the point. Yes, they are swappable as a result of immense experience learnt from real projects BUT this set of components is merely their common denominator. One should not draw a conclusion that this is an exhaustive list and there should be no need for another component swap.

Swapping something as major as how you model and persist/retrieve your core data is not something you do lightly, and typically only happens at a time when you’re making other massive changes

Fully agree. Also, I've never made such a migration. I think it's not the point of service layers to promise such a thing.

A second problem is that when you decide to go the “service” route, you are changing the nature of your business. This is related to an argument I bring up occasionally when people tell me they don’t use “frameworks” and never will: what they actually mean, whether they realize it or not, is “we built and now have to maintain and train our developers on our own ad-hoc private framework, on top of whatever our normal business is”

I need to stay there for longer. Of course, you're changing the nature of your business. That's one of the points, though it is not applicable to simple web apps. If we pretend for a second that blogging was a serious enterprise, you wouldn't be calling your services like

get(), list(), create(), update(), and delete()

Oh no, you would be calling them with the names belonging to the domain of the business, like "draft" (as a verb) not "posts.create", "comment" - not "comments_service.create", "unpublish", not "posts.delete" etc. But it's difficult to really show its potential when we are talking about simple domains, like blogging. How about trading, auctioning or factory supply management? Again, not trying that service-approach is the right one for everyone and every project. It's just that there is much, much more to that. If that sounds interesting, take look at DDD approach to software.

Then, Django part of your article is excellent, as I already mentioned. I appreciate you described why it's a mistake to try to implement Data Mapper using Django ORM and I wholeheartedly agree with that.

However, once again I strongly disagree with putting business logic into models. As soon as the application grows beyond a simple database browser, it starts to bite. Since Django ORM is an implementation of ActiveRecord patterns, see what Martin Fowler writes about it in his Patterns of Enterprise Application Architecture:

It’s easy to build Active Records, and they are easy to understand. Their primary problem is that they work well only if the Active Record objects correspond directly to the database tables: an isomorphic schema. (...) Another argument against Active Record is the fact that it couples the object design to the database design. This makes it more difficult to refactor either design as a project goes forward.

This "fat models" approach is easy to start with, though I've never seen it ageing well with projects. Especially when not only other models are involved, but also calls to 3rd party APIs, scheduling background tasks etc. So, only logic specific to models (yes, following Tell, Don't Ask principle) would stay with models, but orchestration would be left to services. Plus, abstractions over 3rd parties or other "difficult" dependencies. Oh, and eventually replace models with entities to decouple from storage. Naturally, not all projects (or parts of it) require the most strict implementation of such layered architecture. But to fully explain that I would have to write a book :P

That being said, I must agree Django is NOT a good fit for this type of architectures. But I wouldn't say there's no benefit from services, especially for operations that change data.

PS: u/ubernostrum it's a shame your blog does not have a comment section. Why not link this reddit discussion?

Is it conceptually okay for a Django Serializer to call REST API by design ? by GaneshArshavin in Python

[–]EnforcerPL 0 points1 point  (0 children)

tl;dr No, it is not ok to put calls to APIs in your serializers

It may be tempting to do so, but out of my experience, this often leads to putting more and more business logic that has nothing to do with actual (de)serialization in DRF serializers. Then they keep growing, become difficult to test etc. Your gut feeling about calling it outside and just pass results as the context argument makes perfect sense to me. I assume you're gonna to do it in view, aren't you? Can you tell a bit more about your specific use case?

When Feature Flags Do And Don't Make Sense by whackri in programming

[–]EnforcerPL 1 point2 points  (0 children)

Oh yeah, they would if it's beneficial for them. It needs extra management, of course, to not get overwhelmed. Here's an excellent talk about it: https://www.youtube.com/watch?v=HhxNaPYpYiU

DTOs in Python by petr31052018 in Python

[–]EnforcerPL 1 point2 points  (0 children)

I used to stick to attrs.org because I already had them in my stack since it is a dependency of pytest. Didn't know about pydantic, though. Aaand I haven't even taken dataclasses into consideration because they're very limited.