Transaction script is Antipattern?
Asked Answered
P

3

40

Well there is a similar topic about transaction script with NoSQL database, but this one is about the pattern in general. From what I find about Transaction script, it is not object-oriented at all. Its basically procedural code despite the fact that it may use objects in every line of its code.

The better solution is to use a domain model instead, coupled with either active record or a data mapper with unit of work/identity map/lazy load/query object and such. Transaction script may be easy to use, but it is really procedural programming and therefore should be considered an antipattern in object oriented world.

What do you think? Do you agree with transaction script being antipattern? Or do you actually have a way of designing a transaction script that is object oriented instead of procedural in disguise? I doubt this is possible though.

Prevocalic answered 22/4, 2013 at 5:23 Comment(0)
B
65

Transaction Script is definitely not an anti-pattern.

From what I find about Transaction script, it is not object-oriented at all.

You are right, it is not, indeed. That fact however doesn't make it an anti-pattern. Although it is a procedural approach, indeed, it still has its right place in the series of business logic architecture patterns - you just have to know in which case it is a best practice to use it - and in which case it isn't. Simply put: if your problem domain is very simple then it isn't worth the overhead to use a more complex pattern in your business logic.

Or - as Fowler writes:

When to Use It

The glory of Transaction Script is its simplicity. Organizing logic this way is natural for applications with only a small amount of logic, and it involves very little overhead either in performance or in understanding.

The anti-pattern you might think about is called Anemic Domain Model. This is the case when you intend and think you are building a Domain Model - because your problem domain is complex enough for that, - but you actually end up in a Transaction Script - because of bad code organizing / weak OO skills.

Babylonian answered 6/5, 2013 at 14:19 Comment(2)
What you say is completely true, but in my experience every time I have come across the Transaction Script pattern it was a total mess created to make up for the anemic domain model. Call it guilt by association, but when I see this pattern, I know its a problem.Arrears
@HDave +1. Most of the time Transaction Script is not applicable, IMHO, and you're better off with a proper domain model. One case where Transaction Script is OK would be some kind of cache-like service that only stores copies of entites whose business-logic is implemented in other (micro)services.Jackofalltrades
H
16

It's not an anti-pattern. In fact, most enterprise applications (all I have seen) use transaction script and not a rich domain model pattern.

Active record pattern you mentioned is only handy when you have rather simple one to one mapping of domain entities to persistent store aggregates (RDBMS tables).

Data mapper is something like ORM (Hibernate and friends). If your business logic resides within domain entities, these entities must mutate themselves. In my opinion, this couples logic which mutates state (which is inherent when you use ORM) with the state itself. It's simpler to look at your domain model from outside and put your business logic into services (transaction scripts). Also, if your business logic volume is large, it's harder to find relevant code when it's scattered across domain entities (it's like having your transaction scripts mixed together).

But you don't have to end up with completely procedural approach since you can (and should) decompose your services into self-contained highly cohesive 'procedural containers'.

Hardesty answered 14/8, 2013 at 12:13 Comment(3)
Yep, most enterprise applications I've seen used transaction script... and in almost all cases its gone completely off the road as more complexity was added. In most cases it was because of TS, when just a little DDD would have solved so many problems... Therefore I hate TS because its an easy pattern to start with, but often developers are missing the point where it would have been necessary to push business logic down to the domain model... I suggest to only use TS in very simple cases somewhere between CRUD and very little business logic complexity.Institutional
One million percent agree with @Pabzt. Complexity grows in applications. Sticking to transaction script for the whole course for me makes it an anti pattern. I have seen projects with hundreds of services and models where all logic lies in the service layers. Put them all in a Service folder and hey presto!Almonte
Most enterprise applications I've seen use transaction scripts....but they don't know so teams keep talking about DDDChronology
A
2

TS isn't OO or non-OO. You can use it within Domain model methods, service methods, or high-level app methods. It just means you can read the business intent of the program without winding thru a million callbacks and 'black magic'.

That's why microsoft introduced async/await. It turns obscure looking send-a-callback (delegate) and exit, process-the-callback-in-separate-method (required) style - into a readable transaction script.

GOTOs are bad because they break the readable flow of a transaction script, making it a bad one.

a) Transaction script gone wrong is an antipattern. For example, one huge method, no or few method calls, etc. different levels of operations in the same method (refactor them to methods). Discrete steps of the business process together in one method (break them into methods or separate classes. Lots of business objects? Use DDD service pattern).

b) NOT using TS correctly is an antipattern. For example tons of inter-app messaging, event firing, etc so you can't read thru and see the business process (functional requirement for tech apps). Low level details (tech) mixed with functional work. Over separation of a business activity that should be visible on one page.

TS usage should be fractal, with each zoom in drilling down to more details TS style logic. High level: you see method calls and DDD service use. mid level can be a bit of a mix. Lower down is mostly domain object method / property calls and in there the finest logic details.

Throwing TS under the bus because it can be abused, or preventing its use, just kicks the can down the road - the dev that can't group and separate and doesn't know SRP (single responsibility) / Cohesion will screw up other styles, too. The answer is to train them on the business process and give examples of grouping and separating - which should be done by business/functional requirement (vertical slice) not technology (horizontal slice).

  1. put logic that just deals with one domain object or other instances of its type in the DO. Don't reference other object types from domain objects (person.orders) or inject anything into a domain object.(other DO or repository, etc). It violates SRP simple as that. [low level transaction scripts in the methods]
  2. When you need something like person.orders, or feel like you need to inject something, make a DDD service (not serialized, no persistent properties after each use). Inject e.g., a person, and the others collection (repository or IQueryable etc). Do the work there. [mid level transaction scripts here]
  3. combine operations on domain objects and DDD svcs in an 'app methods' category of DDD services.
  4. construct and call those from the highest level of the program

at each level, it looks like a TX script, follow the rules, though. Keep methods small. You will be able to read it then!

Note: In the link provided in the other answer, Fowler tells you how to make a transaction script right vs. wrong:

https://www.informit.com/articles/article.aspx?p=1398617

He also does suggest that it isn't OO. I think you can hybrid it to OO and use TS pros (readability and a hundred pros of that), and the hundreds of OO pros too. That is to say, you can put TS elements in a domain model, and compose domain model use in a higher level TS.

Also consider the definition of a transaction script as a single database transaction. Since your domain model should NOT have the repositories injected (inject domain objects into the repositories), you can actually organize it like that, calling the relevant repositories to (de)persist at the highest level. But if that's not the case, the point is to have a stream of readable code that is not over-separated.

The problem with lambasting TS is it makes people think SRP is all about SoC (separation of concerns) and they never have to worry about Cohesion (keep same things together, which implies SoC too but demands organization). Thus well intentioned engineers just separate things into a million pieces (because more is better) and it's tougher to discern the logic.

Arda answered 7/10, 2020 at 17:29 Comment(3)
What you're suggesting is pure TS and procedural programming. You keep referring to domain objects and DDD, but that's completely misleading. One of the core ideas of DDD is designing around aggregates, which are a graph of domain objects with rich behaviour that implement business logic while protecting business invariants. Your advice is going completely against that. It's okay to advocate for TS. It's not okay to mislead readers by talking about DOs or DDD when all you're talking about is a bunch of procedures operating on an anemic domain model.Alacrity
SRP & Cohesion > Anemic Domain antipattern. If one domain object 'knows about' another (I mean serializable / business entities, not Service objects that aggregate multiple DOs), that weakens Cohesion (and other bad things happen like now you have to mock to test, etc, etc). I know the whole industry does person.orders, and I see its appeal as much as you do., But in the 70s, they all did GOTOs. If you use structs, that's anemic. If Anemic means violating SRP and then it has to go away (or be redefined) as an antipattern. DDD is also 20 years old, it can evolve....Arda
Hierarchical databases were also once essential and deemed convenient too. This is the analogy of D.O. interreferences like person.orders. It seems better on the surface to hard code the relations. AND it's more performant. But in the end that's not where the higher costs are, and it's a losing game.Arda

© 2022 - 2024 — McMap. All rights reserved.