2

is there anything wrong with writing code in a way that merges view, repository, controller in the same file?

i.e.

global $db;
// or $db = DBSingleton::get_db_instance();
// or global $container; $db = $container->get(Db::class);
$id = filter_var($_POST['id'], FILTER_VALIDATE_INT);
$record = $db->parameterized_query("SELECT data from table where id = ?", [$id])->getResult();
foreach($record as $row)
    echo $row['data'] . PHP_EOL;

etc. basically everything is in the same file and there is very little, if any use of dependency injection, and any view code, any database access code, and any "reading variables from HTTP Request object", are in the same file.

Why are modern frameworks advocating against this style? What are the things that can go wrong if I choose to use this way of coding in my work?

  • I just can't get past the SQL injection vulnerability here: $db->query("SELECT data from table where id = $id"); --- using string interpolation (or concatenation) for an unsanitized value taken from user input. Please tell me prepared statements are used in real code... – Greg Burghardt May 1 at 11:34
  • @Greg - updated example – Dennis May 1 at 13:14
5

There's nothing wrong with it. You just lose Separation of Concerns, that's all.

You can write single-page applications in precisely this way using Vue. Code folding in Visual Studio Code insures that the thing stays readable.

But the practical limit of such pages is about 20,000 lines of code (including the HTML). That is a lot of code in a single file, and it limits you to pretty small applications before you run out of architectural air. Then you wind up splitting your code into separate files anyway.

For anything but the smallest of applications, stick to an architecture that allows your application to expand naturally as your needs grow.

  • Thanks. What does losing SoC mean in practical terms? From your answer it doesn't seem like it is a big deal to lose that principle. Are there any gotchas or pitfalls to watch out for or will I really be fine to just not worry about separation of concerns in my code? – Dennis May 1 at 13:37
  • 1
    Didn't I already explain that? It limits your ability to write easily maintainable applications. – Robert Harvey May 1 at 14:47
  • I didn't connect file size limitation to SoC, so it was not clear. – Dennis May 1 at 17:21
  • I didn't specify a file size limitation. – Robert Harvey May 1 at 17:36
4

Merging all code into one file, and not utilizing functions (as your code example suggests) also removes the advantages you gain with abstractions. Even a function is an abstraction that when properly named encapsulates not only logic, but an idea that can be understood by a programmer by simply reading the name instead of spending the time to understand the logic.

Programs written this way are very linear, and code reuse is virtually impossible.

Introducing functions helps with code reuse and allows you to leverage abstractions, when compared to applications that do not utilize functions. At that point you've entered the realm of procedural programming. But there are limits to this as well.

A mishmash of global functions in a single file becomes difficult to manage and keep track of in your head, unless you organize functions into groups. At this point you start to see clear delineations of functionality appear, and you'll likely pull these function groups out into their own file. Then you see a need to bundle data along with these functions, so you start declaring global variables in these other library files. You've now reached the threshold where object oriented code becomes much more useful.

You can say this is a natural progression of low complexity to high complexity, however before writing a single line of code you need to determine how big this thing might get. Most applications grow beyond "a single file" very quickly, causing you to restructure the application multiple times, which slows down development unnecessarily.

Our challenge as engineers is to build an architecture that is appropriately complex for the problems we know need to be solved, and given our experience with how applications evolve. A majority of applications won't fit into a single file without making you go cross-eyed, which lends development to a minimum level of complexity (at least separate your concerns).

You need an architecture where you can easily add features and modify existing features, at the same time not building features and abstractions you don't yet need. That's the challenge.

  • Thanks. I didn't want to take it as far as to use no functions, but what you have said makes sense. My question was born after mechanically extracting various code pieces from one such flat file into various layers, such as repository/factory/view/config/service/controller/entity, each of which is essentially separate function that is encapsulated into its own class, and hence its own file. And after doing this a few times, I thought - why do I keep doing this in the first place, and should I keep doing it? – Dennis May 1 at 13:58
  • If you're encapsulating single functions in single classes, you're doing it wrong. – Robert Harvey May 1 at 16:40
  • there is a movement of "one action per controller", where controller or "request handler" is geared towards having one method per class. For small requests, it ends up being one method in request handler, and possibly more than one method in supporting classes, such as repository, service, other – Dennis May 1 at 17:24
  • That only makes sense to me if you have a need to dynamically specify controller actions at runtime, i.e. the Strategy Pattern. – Robert Harvey May 1 at 19:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.