It would be awesome if I could take code that is really easy to read/understand and safe to modify, and wrap it in an abstraction that adds no value other than "'leaky abstraction' bad" and "unit tests"
^^^ said no one ever
This is apparently "bad".....
C#:
var sshKey = await _applicationContext.SshKeys
.OrderBy(x => x.Id)
.FirstOrDefaultAsync(x => x.Fingerprint == fingerprint, cancellationToken);
This is apparently "good"....
C#:
var sshKey = await _sshKeyRepository.GetByFingerprint(fingerprint); //"shared" code that is actually used by one thing, amazing
This is apparently "bad".....
C#:
var users = await _applicationContext.Users
.Include(x => x.SshKeys) // there was business requirement change that in this feature we also wanted to have the SSH keys for a user, so we just added this `.Include`
.OrderBy(x => x.CreatedDate)
.ToListAsync(cancellationToken);
// do some business logic with the users
This is apparently "good".....
C#:
// var users = _userRepository.GetAll(); // We absolutely cannot modify this existing method, as it will "infect" other code that is already using it.
var users = _userRepository.GetAllWithSshKeys(); // more "shared" code. I bet this version of the repo ends up with many "GetAllWithSomeAndSomethingElse" variations. Or we build "senior" code that uses `Expression` and QueryBuilders, and basically build a duplicate, but WORSE version of EF Core.....
// OR
var users = _userRepository.GetAll();
var sshKeys = _sshKeyRepository.GetAllByUsers(users); // wow this is cool
// OR
var users = _userRepository.GetAll();
var sshKeys = users.Select(_sshKeyRepository.GetAllByUser).ToList(); // kill me now please
// OR
// Some variarition of the above.
// do some business logic with the users
This is apparently "bad".....
C#:
// Lets create 2 separate data models in a single feature
var product = new Product {
Id = Ulid.NewUlid(),
Name = "awesome product",
};
var something = new Something {
Id = Ulid.NewUlid(),
Name = "awesome product",
};
_applicationContext.Products.Add(product);
_applicationContext.Somethings.Add(something);
await _applicationContext.SaveChangesAsync(cancellationToken);
This is apparently "good".....
C#:
// Lets create 2 separate data models in a single feature
var product = new Product {
Id = Ulid.NewUlid(),
Name = "awesome product",
};
var something = new Something {
Id = Ulid.NewUlid(),
Name = "awesome product",
};
// I have no idea how to sanely do this using "repositories"
_productRepository.Add(product);
_somethingRepository.Add(something);
// how do we save now? Genuinely curious?
_productRepository.Save(); // Do we just do this, and because we "know" the same DB context is injected that both will get saved?
// or do we need to do something more convoluted?
_transactionProvider.StartTransaction(_productRepository, _somethingRepository);
try {
_productRepository.Save();
_somethingRepository.Save();
} catch (Exception e) {
_transactionProvider.Rollback;
} finally {
_transactionProvider.Commit();
}
I don't think these are contrived examples.
These "maybe" are naive implementations of ORM repositories, but I am not so sure
I use repositories all the time, for useful things
C#:
public interface IFileRepository {}
public class S3FileRepository : IFileRepository {}
public class LocalFileRepository : IFileRepository {}
There have been quite a few changes. Anyone who worked with the older EF remembers how crap they were. Then Dapper had its shining moment, then NHibernate, now we are back on a relatively stable EF core. Besides, having EF Dbsets in your Domain or service layers really doesn't seem to be in keeping with clean separation. Just saying.
Why would you have `DbSet` anywhere in your code besides in the declaration for you DbContext?
I bet all these code bases have "duplicate" "data" and "domain" classes.
e.g. a "Product" that is used in DbContext, and a "Product" that is in the "domain".
They then have a mapper/automapper for each direction.
This is then considered "clean" or "separated", but meanwhile they are just 1:1 hard dependencies, and every time you go in and out of a "layer" you need to convert.
What a complete abomination of clean code.