Closing session
Published June 13, 2025
This video features Paul Wolf at DjangoCon Europe 2021 in Online.
Clean Architectures and related architecture patterns like Hexagonal and Onion architecture are intended to provide more maintainable code and lower technical debt.
Two parts of Django's architecture, the Django ORM and 3rd party Django REST Frameworks, make it difficult to get the benefits of a Clean Architecture. We look at ways we can achieve the benefits of Clean Architecture (CA) while using the Django framework.
What are the various Clean Architecture Patterns and what do they promise to do for you?
What is the ideal architecture pattern that Django supports?
Problem 1: most Object Relational Mappings including Django's do two things:
Specify the persistence model: normalisation of data, efficient storage, efficient lookup, etc.
Specify the business entity domain: what business objects does the domain manage
The problem is that these are two different goals handled in one framework component, the ORM.
Problem 2: REST frameworks have a heavy reliance on the ORM. This ties together the business domain to storage semantics making it hard to achieve some of the benefits of a Clean Architecture.
Two solutions paths exist:
Django can serve a clean architecture-like paradigm, under specific circumstances. But there is some confusion about what CA looks like in practice that causes developers to go for solutions that are the opposite of CA.
The other solution is a more fundamental rethink of how to implement and use ORMs and REST frameworks (including remote request frameworks like GraphQL).
Paul Wolf argues that Clean Architecture is mainly a way to manage change by making dependencies point from volatile framework and infrastructure details toward stable domain logic. Django’s ORM is productive but combines domain modeling with database schema, state management, caching, and other details, so it does not by itself represent a clean domain model; nevertheless, Django remains a good choice when its models closely match the data presented by views. He recommends isolating integrations, avoiding framework-dependent objects such as ORM instances and request objects in business logic, using context-free Python objects, factories, and dependency injection, and testing code through the REPL and management commands. The trade-off is deliberate: Clean Architecture requires more design and structure up front, while Django is optimized for getting started quickly, so the two should be combined selectively rather than treating either as an all-or-nothing approach.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Speaker 1: So everyone, I am talking about clean architecture with Django. Just a little bit about me. I'm a CPO at a company in London. It's an ad tech company. We use Django a lot. And I've used Django since the late 2000s. For at least, I would say 14 years I've used Django. So I know a little bit about it. I've done a lot of projects. And I've had to grapple with architectural issues a lot. It hasn't always been that easy, especially when you're in an environment where your requirements are changing a lot. And I think that clean architecture is geared to
Speaker 1: deal with that kind of situation. So Clean Architecture is uh uh a book and a set of ideas by uh Bob Martin I think the book was published a few years ago, but it really represents a collection of ideas that have been around for a long time. In the description for this talk, I mentioned hexagonal architecture and onion architecture. I'm not really going to discuss those explicitly. I do have links to those in the references at the end. The reason I won't go into those specifically is because of time, but I'm going to treat these all as the same kind of architectural argument about how systems should be designed. Now just to uh establish a frame of reference, we're we're talking about typical Django applications, uh
Speaker 1: which are web applications, and they have a relational database. A lot of people reasonably try to use other backends like MongoDB or other kinds of storage backends, but we're going to assume a pretty standard approach to Django. using a database, some transactional logic, and views, Django views, regardless of whether you're using templates or a JavaScript front end. Django has a lot of other stuff, and these are reasons to use Django, like the admin interface, user uh authentication framework, migrations. and all that stuff. But but people, the typical way of using Django
Speaker 1: is to select it because of this ability to build your data models and expose those through views very quickly. Uh clean architecture is intended to reduce the cost of change. make things uh easier and faster, uh get independence of your solution components, uh better testability and reduce cost. Now that sounds like a list of all the good things that you always want. But there is something here that's rather important about clean architecture, which is it is about managing change over time Uh and there's a a a very important part of this, which is that it's mostly after an initial investment in design.
Speaker 1: I think that the primary idea that one needs to get hold of is this idea of the arrow of dependency And I I'll just quote here from Bob Martin. The high-level modules should not depend on low-level modules Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions. Clean architecture is for making it easier to change things. So This is very schematic view of this central idea, which is that you have volatile parts of your code base
Speaker 1: and they depend on more stable parts of your code base So looking at it in the context of uh patterns in general, this dependency rule, which I have at the top here It's a very general rule that that really governs everything else and is a more general thing than some of the others. The things at the bottom, like model view controller or model view template, it's not that they're irrelevant, they're just less important for this perspective than these higher level uh concepts This is a kind of ubiquitous uh diagram that you'll find on the web everywhere. I I am going to show it to you, even though I'm not going to return to it exactly in this form.
Speaker 1: But just so you have it for reference, in the middle is this more stable part of your code base. Going towards the outside, you have less stable parts of the code base and you want these arrows as shown on the left there to be pointing towards this uh inner part. So The inner part of these, your domain model , surrounded by your use cases, surrounded by controllers, gateways, presenters, and then all the more incidental detail. stuff, devices, web, db, ui, external interfaces on the outside. So uh going back to this uh more schematic view but with more uh detail uh
Speaker 1: we have databases we have views we have integrations And we want these dependency to be pointing inwards. So very important for uh understanding this in terms of Django The UI mechanics are a detail, which is why the views, and I do mean Django views, or uh Django Rest framework, the same thing would go for that, is on the outer circle here, meaning it's the more volatile part, the more detailed part of your code base. Now uh uh it's really important that they the uh if you research uh clean architecture or you buy the book. Uh you'll notice that uh he doesn't really mention Python more than maybe one
Speaker 1: once or twice in the entire book. Uh it's very uh geared towards uh uh Java and C<unk> put that, but that doesn't matter too much, I think. Um I think if you come from those uh the direction of Java, you're probably gonna uh the first thing you're gonna say is where are the uh interface um constructs in the language of Java. As we all know, Java doesn't have that kind of uh construct So there is a very big uh uh bias here and he he would tend to see design happening up front. involving a lot of interface design. And we're not going to have that in uh Python.
Speaker 1: Another thing that he says very clearly, even though this quote is taken from a different source than the Clean Architecture book. uh it's something that he does refer to very frequently. Architectures uh are not about frameworks and They should not be supplied by frameworks. So your your Django framework design is not your application design. And that's a very important point. that is uh essential to clean architectures. So uh Interfaces are abstractions and most dependencies should be on interfaces, on abstractions. We don't have in Python
Speaker 1: a interface concept or or or language construct. We have other things. We have abstract classes. It's not the same thing, but it can serve as somewhat the same thing. We also have duct typing, which might uh might surprise some people uh that I mentioned that here, but that is one way of implementing abstractions, which is to agree on conventions for interfaces. where you say that uh objects should uh uh by convention implement a certain set of methods, get put, for instance. And this abstraction will work just by using duct typing. The other way is factories, which I'm going to talk quite a lot about. uh and uh dependency injection libraries which i'll also uh refer to.
Speaker 1: So so we're gonna need uh some other uh substitute for uh uh the missing interface construct in python. Now best use case for at Django, this is my opinion. is you have uh basically this schema. Uh but the essential factor is that your models, the way they are put together, they will represent a domain that is very similar. to the way you want to present that domain through your views. And I'll come back to this again uh later. It does get a bit tricky uh when you have a complex business rules uh situation where you need to do a lot of things
Speaker 1: in between modeling the uh uh your domain and your uh presenting that through the views So dependency inversion is a key factor that runs throughout clean architecture. And it's just a way of changing this arrow of dependency if it's pointing in the wrong direction, like if it starts to point outwards from that more stable uh parts of your code base, uh you you you you don't want that and you can use dependency inversion to uh change that. And one way of doing that is using dependency injection. It's a little bit confusing because you see the uh uh abbreviation di everywhere. There are two things here going on.
Speaker 1: Dependency inversion is the uh is the concept and dependency injection is a pattern that you can implement to uh achieve dependency inversion. I think the, I believe the first uh mention of the the the injection pattern is is Martin Fowler. There's also a Python dependency injector framework that I reference here. I'll also mention in the references. I'm not going to use that here because it it's a little bit complicated. And I think in many cases you don't need it, but you might want to have a look at that. Now, uh one of the things about uh I'll just mention here, even though I told you I wasn't gonna go into these other hexagonal and onion
Speaker 1: architecture things. There's a uh a way of expressing um this going from one layer of abstraction to another in Clean architecture, he uses the phrase interface adapters. In the others, it's sometimes ports and adapters. Just so you know, those are synonymous. So we want to use interface adapters to just keep the dependencies pointing in the direction of less volatile, less detail-oriented code. Now uh the uh I I really consider the Django ORM to be a pretty critical part of the Django framework. It would be hard for me to imagine
Speaker 1: using Django without using the ORM. It gives you great uh create write update. uh capability and it lets it's it's it's kind of simple in the way it works. It's easy to understand how to do it. You just you define your data model or domain, however you want to look at that, and you can get going very quickly. And migrations are are are in general pretty easy to use and save you huge amounts of work. So this this this setup in my view works really well. Now it's important for uh with respect to clean architecture to appreciate what the ORM is doing. It's doing a lot of stuff.
Speaker 1: You're modeling your domain. It's doing database connection management for you. It's doing the object state management for you. It's doing caching. And this is a bit of a problem for clean architecture because those are all detailed things, except for the data modeling, domain modeling. I should have put probably data modeling there. You're mixing up things that are supposed to be separate. The domain modeling belongs to the inner circle or the inner two circles. Whereas everything else is the outer layer and you're mixing those up already. So you you've already got a situation that's according to uh the dependency rule is
Speaker 1: somewhat uh mixed. And uh so the ORM has a lot of responsibilities. This schema and data migration, as I said, state management, all that is those are details. And I'm going to give you an example of what I mean by uh In particular with regard to data modeling versus domain modeling. So let's assume in my industry we have this concept of an asset that can be a print asset. Or it can be a digital asset. And I can model that on the left by, as you see, defining a Django model that uses inheritance. Now , I could also
Speaker 1: do the exact same thing by doing the set of models on the right, which is not using inheritance, not using inheritance of the asset model. uh I would just have three models, whereas I'll have sorry, three tables, whereas on the left I'll have two tables. And the point here is I'm deciding I'm making decisions about the data schema, and that is absolutely a detailed decision that really shouldn't have anything to do with the domain. This is really important. So there are also additional limitations to the RM. And this would go for most ORMs, not just Django's. The
Speaker 1: one one of the things that's it's a little bit difficult maybe to um explain without a lot of examples, you your model representation is a snapshot of your domain that's valid for some ways of using uh or supporting a set of use cases, but not necessarily all of them. I also, it's my opinion that's as I think that the query set API is fantastic. I love it. You can do a lot of things with it. In fact, you can do almost everything with it. It's amazingly powerful. And it's also extremely well designed for performance. But but that API does get to be a little bit awkward in introducing quite a lot of things
Speaker 1: like uh outer refs. um um uh F functions uh thing in order to push uh functionality down into the database. It gets a bit complicated and that kind of waters down the argument of simplicity on a simple interface a little bit for me. The other thing is it's not very good at doing certain things like vectorized operations. So if you're using NumPy, for instance, you want to be able to operate over uh large in-memory objects In order to do that with the RM, you need to convert everything to some kind of Python type. And then you can do that. And it doesn't support it directly.
Speaker 1: And you might for some, not all applications, you might need to do this a lot. The same thing with geospatial operations. I I find uh like I said, the query set API is amazingly good. Uh but I find it kind of uh gets more difficult when you get into more uh specialist areas like geospatial operations. There's Geojo, it it works very well, but it often doesn't work well enough to cover all the use cases. The other thing is CQRS, so command query response segregation, pretty awful acronym. uh is uh usually involves two things uh and you can just do the one if you want. Uh reading from a different source than
Speaker 1: uh writing So if you, for instance, use the RM to do all your transactional work, you could use a different data source that has the same data that's optimized for reading. And this brings you outside the object relational mapping in Django. And you have to think of all these things and what your use case is. So I'd like to introduce a concept. This is my naming, and it's just to express the idea that you can have objects. that have that are context-free, meaning they have uh uh no dependencies other than the Python standard library. So they wouldn't have integration state. They have no framework dependency, so they can't be from a framework other than the Python standard library.
Speaker 1: And they have to be very easy to create. And I'm going to use this concept later. That's why I'm introducing it now. Uh so what we could do this. Um if if it's a problem that the uh ORM is uh has these shortcomings in the sense of mixing details with uh the domain, we could say, okay, fine, let's just create an interface adapter on the entities layer. and have that call the RM and we won't see the RM from our views and it will just return uh dicks dictionaries, tuples or or data classes. And we could do that. The only problem with that is it does undermine the
Speaker 1: Django framework. So you're not really using Django in the way it's meant to be used. And I want to come back to this thing that I've said before. This is for me the Django RM use case principle. If the data representation in the view is similar to the model layer, then you are well within the principal Django use case. And that's going to work well for you. You'll get great performance. It'll be easy. It'll be a good experience. And here's here's now a schematic representation of that in relation to the um uh uh normal schematic view of uh clean architecture.
Speaker 1: Uh I have up here a uh that's a view function. You can see the request uh parameter And it's going to go get the average price of all my books and it's going to return that. But you can see that this is not very clean architecture because the arrow is not pointing. inwards. It's pointing to another detail artifact. Now I can change this to do something like this where I have a uh a price engine, let's call it. This is a very simple example, but you can imagine more complicated pricing algorithms. My price engine has a method, getAveragePrice. I in my view function I create
Speaker 1: an instance of the price engine, get the average price. You can see in the view function I 'm not directly interacting with the ORM. Now, here's a more complicated uh view, but it's uh the view that that expresses A way to start implementing uh the clean architecture dependency rule. So instead of The previous one, uh I'll just say first of all, this does the same thing. It does the exact same thing, but it adds one piece of uh functionality which is that it will select a different price engine uh depending on whether it's the summer or not, uh whether it's June, July, or August.
Speaker 1: It'll return a pricing engine that does discounting. So what I'm doing here is I've got a factory function that will be in a different module, by the way And it's going to get me a pricing engine. And I have an interface, an abstract interface that will define what that pricing engine looks like. So that's my abstraction. And in the the view function is at the very end, uh I get the factory, uh, or I get the pricing engine from the factory, which automatically selects uh whether it's a uh discounting uh price engine or not. And then I get the average price. And as you can see on the on the right, I'm uh
Speaker 1: I'm really implementing this sense of abstraction using dependency injection , which you can see in the factory, I'm creating this. Objects through an abstract interface. And that that this is kind of the main thing. And again, I note that the ORM, even though you could say that the part of it is in uh should be the domain definition which is in the entities because of the mi it's mixed up with details. uh it's it's really part of the uh detail layer. So uh here's another example
Speaker 1: Um I have I want to call a reservation, but let's say it's a hotel reservation uh service. It's a um API service. And I've got start end dates. I've got an object in my ORM that's uh called a reservation. It has the start end dates. I now am coming up to those dates and I want to check to see if the reservation, what the state of the reservation is, that is whether the room is free or not. So The only problem with this is if I pass that ORM object to that API, so I've written my API client. And that is using the start-end dates of the reservation object. I'm going to end up with this. And this is a problem
Speaker 1: because we end up with what you see here on the right. which is uh kind of a sketicode effect where everything is related to everybody uh everything else In this particular example, it seems simple, but it will very soon become quite complicated and difficult to maintain. So a better solution would be to again use a service factory, which is going to create our reservation service for us and inject the client for calling the external. uh um api and then i can also uh use this make context free reservation And you'll remember I explained that for me, context-free
Speaker 1: means that it's an object that doesn't have an integration state. It's very easy to create and manipulate. And it's mostly or entirely a Python standard library object, like a data class in this instance. And then I pass that to the integration, my integration client. And you can see there that the result of doing that prevents this cross-detail dependency. And that is going to uh be pretty helpful. So um We we want to try to do that kind of thing.
Speaker 1: The only problem with it is it's going to cause us to use the RM in a way that, as I said before, is not part of the uh standard use case for Django, which is in views calling the ORM, which is the the easiest and uh most frictionless way to use it. Now I've written a project called JAC. It stands for Django Query. It uses the uh it provides a query language based on strings. uh to query the ORM for objects and only returns uh Python standard library objects like uh dictionaries or tuples or lists. Now the the reason I mentioned this here is just to show that you can Break up the ORM into
Speaker 1: its different features. But I will remind you that I showed you on a previous slide that this has the limitation that you're you're dropping the uh standard way of using the uh Django views and ORM. But you could do this kind of thing, okay, or you could use some other mechanism to do that. There are resources out there that would tell you how to do that. REST Frameworks for Django, the the only thing I want to uh say here right now, just because we have limited time, is is that uh they are uh uh very much involved with the RM, so it's very difficult to uh
Speaker 1: kind of Once you're committed to them, they are going to refer to the ORM. And it'll be very unnatural to try to pick that apart if you're using Django Rest framework, for instance. So I have a few recommendations and I make these recommendations based on personal experience. There are a lot of things that you can do in order to implement clean architecture principles. First thing is obviously just to be aware of what those principles are and to appreciate the fact that if you if you try to do things like this you're going to incur overhead that you don't have if you use the standard Django
Speaker 1: use case where you're calling the RM from views. So just remember that's gonna there's gonna be a cost to that. So do try to uh minimize dependence on details, however. So if there's any code that's not part of your business logic code or from the Python standard library, you want to avoid passing those objects. or variables through the call tree. And this is going to enhance your experience very considerably, in my experience. So avoid passing context-dependent variables as far as possible. Try to avoid this cross-detail dependency, except for the ORM Django
Speaker 1: view dependency, which is just part of the Django framework, and it's kind of the reason for using Django. So it doesn't make a lot of sense to uh uh try to get rid of that. Uh don't rely on integrations any more than you have to, and you uh you need to isolate those And just try to use the Python Standard Library whenever you can. And remember that Django is a framework So Django is for doing web things for serving data models that look from the outside, like the domain you've modeled Try not to uh another example of a context-dependent variable is the request object, and this is something that I've seen time and again.
Speaker 1: It stitches if you pass that as a function and it keeps getting passed through the call tree, you're going to be stitching your whole architecture together with this. detail thing and and you want to try to avoid doing that. It is an integration object. Use Jenga the way it was intended. Use dependency injection as much as you can actually. Again, I don't personally use a dependency injection. library, uh but it's very simple to use if you use the the factory pattern. And there's a great way to test it whether or not you're doing this. Uh and I call it the REPL test. So REPL is uh uh gonna be iPython, uh
Speaker 1: Jupyter Notebook. There's a default Python uh REPL of course. And there's the debugger. I mean I use IDB a lot , but it can be any debugger. The point is that your code should easily load into these things and you should be able to uh uh uh import a module and create constructs of those modules without going to a lot of trouble and that is a heuristic test of whether or not you're producing context-free code And the same thing goes, you really want to be able to create Django management commands for testing functionality or doing various tasks. And it's very frustrating when you can't do that because all the interesting uh features are bound up in in views.
Speaker 1: and you're trying to call them from the command line. Unit testing is going to be massively easier if you apply these principles. Mocking is just not going to be as good as when you're using context-free objects. It's just much easier to test. And try to understand how your project's going to change. It can change in one of two ways very generally. One is you can just be add more apps that uh uh uh do more of the same thing, or There might be a growth in functional diversity, and that will be much more complex kind of uh change. Really important here is Django is, and I hope this isn't too controversial, Django is for making it easier to get going very fast.
Speaker 1: And that's the opposite of clean architecture. Clean architecture is the expectation is a lot of initial investment and then everything gets easier afterwards. Now, I think you can combine these two things. So that's something that you need to evaluate for yourselves. Here's a few references to get you going with research. And I want to thank everybody for listening. I got a lot of help from some colleagues and friends. So thank you very much. Okay.
Speaker 1: Um I was on mute by default. Is anybody up here me?
Speaker 2: Yeah, yeah.
Speaker 1: Awesome. So I'm not sure what the protocol here is, but uh if Somebody wants to go ahead and pose any questions.
Speaker 3: Well I I I have a question.
Speaker 1: Sure.
Speaker 3: So Can you uh go in in more detail about why uh a a model instance cannot be considered as an entity At least considered as a GTL.
Speaker 1: Sure, I can. Uh because the um Because models are um they are a domain, they can be a domain instance or a domain uh entity that you're designing. Uh but they're also uh that code also does a lot of other things. Um I showed you uh this one example of having a uh polymorphism for a uh an asset instance that can be print or digital. And there are different ways of defining that depending on what data schema you want to end up with. And you're making those decisions while you're designing your model.
Speaker 1: And this is completely contrary to the idea of clean architecture. The idea of clean architecture is that you design your domain in complete ignorance of the details of how it's implemented. So that's that's very not clean architecture. That inner circle with entities should be purely a description of your domain. It is code, by the way, that we're talking about. It is your code base because we're talking about dependencies of the outer layers on the inner layers of code. There's no question about that. But but the the the assertion that uh
Speaker 1: Bob Martin makes is that you are uh defining code that represents only your domain. And then how it gets populated, whether it's cached, all these other how it is represented in a relational database are totally separate concerns that you shouldn't be concerned about while you're writing that code.
Speaker 3: But isn't that depending on how you write your model, isn't that what you actually do in a model? You you describe an entity and you say this entity has this and this and that property. And that's that's your code. So your code y that piece of code is just describing that, isn't it?
Speaker 1: Well his assertion is that that's not the case because what you're saying is if you have a a book uh uh bookshop uh and you have your model book it has a title it has multiple authors so one one of the things about the uh that is is a great example Uh uh first of all, if you say it has a title, you're saying there is one database field. Uh if you say car field and then max length. You are saying something about the technical implementation in a relational database, which is there's going to be one field for that in a tech in a relational database. If you say I have a now a uh M to M field that is the list of authors that can be anywhere from one
Speaker 1: to any number, you're saying that you want a No field, but you do want an additional table with two fields in it, an author foreign key and a book foreign key So you're making those detailed decisions when you say I have an author's field. And it's a uh M to M field. So that's not really domain definition or or domain description. It is that as well. You're saying that books have authors, but you're also specifying how that's to be laid out in the database.
Speaker 3: Okay, thank you.
Speaker 1: Sure.
Speaker 4: Yeah. I so two things. I wanted to make a comment on that. Uh like there's a a good example of like what your models isn't your domain necessarily. Uh for example you have You can use uh uh the same model uh in different contexts. So your user is a different user for your accounting domain uh than it is for your like delivery domain or for your uh whatever configuration uh use user settings domain. So the the information you want from that user is different in those different contexts
Speaker 1: Uh
Speaker 4: they are all represented by the same database table, but the information you want extract from that is completely different. So if if you treat your model as your domain entity, you end up with a lot of properties uh in your like uh model class uh trying to represent that same database table for those all of those contexts uh and if you have different uh objects uh as entities in all of those contexts you can like separate those concerns uh and doesn't matter if they are they belong to the same database table what's So you you get different information in in which one of those concepts.
Speaker 4: I I think this is a good example of like why they're not the same thing. Uh so but I what I wanted to ask is Like do you do you have any uh examples of like practical uh implementations or or kind of a framework uh of how to link uh the the common uh Django uh objects to like that that schema, for example. Uh 'cause I find it really hard to push to to like usual Django uh uh developers, uh that kind of uh way
Speaker 4: of programming. Uh no normally people find it very difficult to to understand that and and very bureaucratic to introduce those layers between the the common Django stuff we use. So I was wondering if you know any kind of like facilitator to introduce that.
Speaker 1: Unfortunately, I've had the same experience as you, uh, which is I find it very difficult to um uh uh to do this and the the natural um but I think part of the problem is that Django works really well if you don't do that. Uh in other words if you just directly use the uh models And in views, you're using uh like you look at the book, I think the uh Django documentation has a bookshop example and um It's just so seductive because it's so easy to use it that way. And then of course, as you're pointing out, it's it it uh then when you want to say, okay, things are getting a little bit more complicated here. And it would be better if we separated out the layers and re
Speaker 1: recognized that business rules are now being implemented in views and we don't really want them there. And then you have to start separating them. And I I I honestly I d I don't have a I mean I do have kind of a methodology for that, uh, but it's I don't think it's very magical and it's not recipe-like, like here's a list of things to do Um it it but using um uh d factories I think is massively useful for decoupling. Also making sure, for instance, you would have factories in a different module. You wouldn't have them in the same file because you want to reduce dependencies.
Speaker 1: You don't want the dependency to be on the same module or on a different view. uh module that ha ha happens to have a factory that you want to get a hold of. So so there's no there's no magical way to do it. I I I do have a description of uh way to structure uh this stuff in a in a online read the docs article called uh uh it's on my github page and it it will describe uh as an example a way to structure your directories Uh and there will be more discussion of y applying the solid principles. Um I try not to go into too many architecture uh issues there, but it does talk about ways to do that.
Speaker 1: But I I I'm not claiming to provide a recipe uh to fix that.
Speaker 4: Thanks.
Speaker 1: Python coding guidelines uh at uh dot read the docs is the um is the thing.
Speaker 2: Maybe I can um share a little experience that I have made here. Um as long as I was having the good situation that I could just pass through model attributes to the view component. It was pretty straightforward to use the Django mechanism. As soon as I needed to calculate stuff based on different parameters, I ended up in um Yeah, pulling this out into an own entity or logic component to do this number crunching. so that I can reuse this in other places and also change the logic quite easily and stay dry in this
Speaker 2: context. So this might be a quite easy or a quite straightforward scenario where you can think of implementing this clean architecture principle. And we also have services that do a lot of business logic and we um move them into their own component and um via factory pattern uh made them usable in um the different places in our code base. Yeah.
Speaker 1: Uh uh you're describing exactly what I've experienced, exactly what I've uh experienced. So in my company we have a uh we have to do quite a lot of um uh calling out to external services that uh enrich data in the models uh and then we have to do operations that require knowing everything about everything in the entire database. uh of like a um uh an asset uh because you have to do things like give me the top assets that have some data attribute and that data attribute came from a foreign source and this is not really that easy. You can't really do it effectively with the R within the RM, so you end up extracting everything from the RM and then doing everything you need to do.
Speaker 1: And that and then you need to separate out things that need transactions and things that are read-only. So I I've had uh you've kind of described exactly the kind of things that uh I've experienced. Anyone else?
Speaker 4: Yeah. I I've recently like had an experience of trying to Well, I we couldn't really change much of the architecture 'cause like well big code base with not much time. Uh bit but we started like cleaning some surfaces of it. Maybe. Uh so we we kind of started introducing some uh objects, for example, value objects. Where like uh when we when when we need, for example, to like in implement some business logic that involves uh different entities or the different models so like we try to well create this a value object
Speaker 4: that does this all this magical things and and we pass that value objects to to views, uh then extract some some things into services, so things that depend on is external things, they they become services. And one useful thing was like to introduce use cases uh that like have m this this uh orchestrating uh role. Uh But I yeah, uh one thing I wanted to to do was to like uh gather all people that like this this kind of uh thing and try to to build a more consistent architecture
Speaker 4: to like kind of yeah have a guide to like it say I have this like big legacy code base, how do I start to to clean that up with those with those concepts? I I have the impression that everyone does a little bit uh uh There's not like a a great solution where you just jump right into that and and starting from
Speaker 1: it very universal. You said at the beginning that you had uh A big job but with little time. Django is fantastic for that situation. Um, it really is. It's just that as things evolve, it gets messy and then you have to uh uh start organizing things as as you just said. And uh I think that you can continue it's not like you have to get rid of Django and then use something else. I don't think that's the case. I mean what what I'm uh doing with my colleagues is we're using Django for the things that it's really good at. And then using other parts of the architecture, we're using other things. Like we we use a CQRS kind of architecture. But for for straightforward objects
Speaker 1: where like I said the objects the way they're modeled look like the views that we want to present, then it's no problem. For other things that won't work, but you can use both. And the things that Django does well, it does really well. Okay , we're getting down to a few people. Anybody else? Okay, maybe maybe maybe we're done. If if no one else has anything. I I'm I'm very grateful for the for the comments, by the way. Thank you so much. And thanks for listening to that.
Speaker 2: Thank you. It was very interesting.
Speaker 1: Thank you. Okay. I'll see everybody.
Its main purpose is to reduce the cost of change over time by making components more independent, improving testability, and isolating volatile implementation details. This usually requires an initial investment in design.
Discussed at 2:30High-level modules should not depend directly on low-level modules; both should depend on abstractions. Dependencies should point inward toward the more stable domain and use-case code, while details such as web interfaces and databases remain on the outside.
Discussed at 3:19Python can use abstract base classes, duck typing, factories, or dependency-injection libraries as substitutes for a formal interface construct. In the talk’s examples, a factory creates an implementation behind an abstract pricing-engine interface and injects it into the calling code.
Discussed at 8:03The ORM is useful because it combines data modeling, migrations, persistence, state management, and caching, but most of those are outer-layer details. Putting them in the same code as the domain model mixes concerns and makes the domain dependent on database implementation choices.
Discussed at 12:47It works well when the model representation is close to the data the views need to present. In that situation, directly using Django’s ORM from views is fast, simple, and performant, and trying to separate every layer can add unnecessary overhead.
Discussed at 18:56Use a factory to create the service and inject the external API client, then pass the service a simple, context-free object such as a dataclass rather than a Django ORM instance. This prevents the integration layer, ORM, and domain code from becoming directly dependent on one another.
Discussed at 23:32Use the “REPL test”: modules should be easy to import and their objects easy to construct in a Python REPL, notebook, or debugger without setting up the whole application. Context-free code is also easier to use from management commands and substantially easier to unit-test and mock.
Discussed at 29:41Django is optimized for getting started quickly by putting models and views close together, while clean architecture expects more up-front design work so later changes become easier. They can be combined by using Django where its conventions fit and introducing cleaner boundaries only where the domain or integrations require them.
Discussed at 31:17A Django model combines domain concepts with database decisions and framework behavior. Field types, lengths, relationships, persistence, caching, and state management all expose implementation details, whereas a clean domain entity should be defined independently of how it is stored or populated.
Discussed at 32:47There is no universal recipe, but you can keep Django for the parts it handles well and extract business rules, value objects, services, and use cases where complexity grows. Factories in separate modules are especially useful for decoupling, and the architecture can combine ordinary Django patterns with alternatives such as CQRS.
Discussed at 39:51Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025