You basically can. The difference is that it's quite obviously notable to a developer of any level that changing the schema in the database is something that should be done with some forethought and care.

And if they change the schema in Mongo, with the official C# driver, by default, any program that tries to read the collection<T> and the document in the databases doesn't match the C# class, you'll get a loud and obvious error at runtime.

Of course if you want to have the flexibility of having objects change in the data store not affect downstream changes, you can decorate your class with [BsonIgnoreExtraElements] attribute.

You'll get all types of
And then when that change is made, all applications accessing the database get the same new view of the schema so you're not going to have two different clients with a different idea about the schema trying to operate on the database at once because schema is global.

My contention is that you should not have two "clients" operate on the same schema. All clients should be using one module. I'm not advocating that microservices are the one true way. I'm saying that all clients should be using the same module, microservice, or stored procedures (well I hate stored procs for that type of stuff, but that's another rant)

Making a change that suuuuubtly alters the way data is stored in an unstructured object is the kind of thing that's really easy to overlook in a pull request.

There is nothing "unstructured" about a data class (POCO/POJO) in a strongly typed language. Yes you can operate on a Mongo Collection in .Net by getting a Collection<BsonDocument> but most of the time you're going to get something like a Collection<Customer> and the compiler is not going to allow you to insert, search, etc. anything but a C# defined Customer with C# defined data types.

Your "only one module" rule is laudable, but you've also got to make sure that module can do everything you'd ever want to do to the database,

That's the beauty of .Net, Linq, and expression trees. You can define your repository with a method of something like:

Find(Expression<Func<Employees,bool>> query) {...}

and the client can pass in any arbitrarily complex, strongly typed, Linq expression and your repository can pass that expression to the Mongo driver and it will translate that to Mongo query. It gives you even more flexibility because if later on you decide to use an RDMS, those same client queries can be translated to Sql by the Entity Framework driver.

But even if you don't want to go as far as "one true module" for your data, there is no reason not to have one module that defines your data types. Anyone querying against or inserting into a collection<T> will always have the right document definition of course you can put all of the definitions into a Nuget package and it will still be obvious when changes were made because your nuget package will be versions.

including all the "one off" data mangling admin tasks anyone occasionally has to do.

A one off type of admin task wouldn't be part of your production flow and you would probably bypass any abstraction that you had - including stored procedures, views, etc.

* Otherwise it will get bypassed. Case in point I used to use an ORM heavily, but it wasn't possible to express everything in that ORM, so where that broke down we had to resort to manual sql. Add a few developers to the equation and you don't know exactly the format of your data.*

There should always be one team responsible for your business objects (in DDD your aggregate roots). Why would you let any one developer change your underlying data store without a code review and/or pull request?

Good schema management practise also means you end up defining all of your schema mutating operations as migrations, leaving you with a rather vital log of how things have changed over time and a good pinch-point to catch inadvisable changes to schema.

If I'm using a strongly typed language like C# and I'm defining my documents based on a class. My Employee.cs file (and all of the child classes) are in sync with my Mongo document store. The language ensures that at compile time. "knowing my schema changes" is simply a matter of doing a "git blame" on the Employee.ca file.

Even if I'm not abstracting all of the data retrieval in a repository, I don't see any reason not to at least have a Nugst package of all of my POCOs that define the documents in my collection.