À l'été 2015, j'ai rejoint une startup d'IA. C'était un développement décent pour la première fois en 5 ans.
J'utilise Python Django (plus tard Google App Engine de Python). C'est un framework très similaire à Rails. J'ai touché Rails il y a environ 7 ans. J'ai donc pu suivre l'idée du cadre.
Cependant, le code réellement en fonctionnement était terrible. Et le code de l'ingénieur serveur qui a rejoint l'entreprise il y a environ un mois contenait le charme de Medapani.
Vous trouverez ci-dessous une liste de symptômes visibles.
J'étais confus.
J'ai pensé à la cause.
Je ne peux pas concevoir et analyser en premier lieu
――Il n'y a pas du tout de document disant "car cela changera plus tard"
Je ne connais pas les modèles d'architecture d'application d'entreprise
--Il y avait un putain d'ingénieur qui voulait connaître l'architecture uniquement avec Rails «Non, je sais que Rails a un bon ingénieur. .. ..
Le directeur est un vendeur et est trop ignorant de la fabrication et de la fabrication de produits informatiques. L'un des anti-modèles de démarrage est que les directeurs des ventes commencent sans une bonne compréhension du secteur technologique.
La cause 1 a tenté une session d'étude du processus ICONIX. Cliquez ici pour le livre de référence.
Parlons de la solution pour la cause 2.
C'est trompeur, mais le modèle Active Record n'a pas commencé avec Rails. Il est décrit dans [Enterprise Application Architecture Pattern] de Fowler (https://www.amazon.co.jp/dp/B01B5MX2O2).
Je comprends que les tables View et DB sont 1: 1 et que View: Model: Table est 1: 1: 1 si vous incluez le modèle entre les deux. Il est adopté lorsque la configuration simple, le service simple et les fonctions compliquées ne sont pas ajoutés.
Cependant, au fur et à mesure que le système devient plus complexe et que les tables et les modèles se développent, cela devient difficile. Il est facile de se tromper.
Bien qu'il chevauche les symptômes ci-dessus, je pense que le problème peut également être classé en plusieurs modèles. Le contenu correspondant au modèle de problème est décrit ci-dessous.
J'ai limité la vue et l'interface par lots à seulement trois choses.
--Validation des paramètres de requête et d'argument
Le terme architecture orientée services est utilisé ici. Un type si ordinaire.
Modifié pour exécuter les méthodes de couche de service à partir des vues et des interfaces de lot
Le contrôleur d'application est applicable dans la conception pilotée par domaine. Notez qu'il est différent de ce que l'on appelle un service dans une conception pilotée par domaine (voir Classification Evans).
Il existe également un modèle de présentation Web dans le modèle d'architecture d'application d'entreprise, qui possède également un contrôleur d'application.
Oh, c'est déroutant.
Fournit une API grossière pour les vues. Les règles de dénomination sont divisées selon les deux modèles suivants.
--CRUD: XxxService avec un seul modèle (tableau) --Combinez plusieurs modèles (tableaux) CRUD: YyyEngine
Il est également inutile de créer une instance à chaque fois que vous l'appelez depuis une vue. Recette pour la singletonisation avec le décorateur de classe a été adoptée.
Si JOIN est plus rapide dans le traitement par lots, il peut être librement utilisé dans un modèle étendu avec un QuerySet combiné. Décrivez le cas où la clé étrangère est apposée.
Parfois, j'ai créé un modèle avec des informations supplémentaires sur l'élément plus tard.
Traitez ces données JOIN comme s'il s'agissait d'un modèle.
Ça ressemble à ça.
models/item.py
class Item(models.Model):
name = models.CharField(max_length=128, blank=True)
description = models.CharField(max_length=1024, blank=True)
image = mdoels.ImageField(upload_to='hoge', blank=True, null=True)
models/item_feature_1.py
class ItemFeature1(models.Model):
item = models.ForeignKey(Item)
feature_1 = models.TextField()
models/item_extra_info.py
class ItemExtraInfo(models.Model):
item = models.ForeignKey(Item)
info = models.TextField()
QuerySet pour rejoindre ressemble à ceci. En supposant que vous rejoignez select, rien d'autre n'est pris en compte.
joined_query_set.py
class JoinedItemFeatureQuerySet(models.QuerySet):
def __iter__(self):
queryset = self.values(
'id', 'name', 'description', 'image',
'itemfeature1__feature_1',
'itemextrainfo__info')
results = list(queryset.iterator())
instances = []
for result in results:
item_feature = self.model(
result['id'],
result['name'],
result['description'],
result['image']
)
item_feature.feature_1 = result['itemfeature1__feature_1'] #Ajouter / emballer les propriétés du modèle
item_feature.info = result['itemextrainfo__info']
instances.append(item_feature)
return iter(instances) #Reconditionner au protocole de l'itérateur
self._fetch_all ()
et de le mettre dans self._result_cache
.custom_manager.py
class JoinedItemFeatureManager(models.Manager):
def get_queryset(self):
queryset = JoinedItemFeatureQuerySet(self.model, using=self._db)
queryset = queryset.filter(del_flg=False, itemfeature1__isnull=False, itemextrainfo__isnull=False) #Ne soyez pas nul
return queryset
joined_item_domain.py
class JoinedItemFeatureDomain(Item):
objects = JoinedItemFeatureManager()
class Meta:
proxy = True #Ne créez pas de tableau.
Vous pouvez utiliser les données librement avec join_item_features = JoinedItemFeatureDomain.objects.filter (...)
. .. .. Haz.
Il peut être découpé en tant que stratégie ou réutilisé en tant que classe de calcul uniquement.
――Pour les choses qui peuvent être réutilisées plus tard ou qui peuvent être transformées en bibliothèque unique d'une entreprise
Dans Django, il existe une méthode appelée modèle proxy, et je la place dans une sous-classe.
Ci-dessous, un extrait du code de mon projet personnel.
intro/models/abstract_model.py
from django.contrib.auth.models import User
from django.db import models
class AbstractModel(models.Model):
registered_at = models.DateTimeField(auto_created=True)
registered_by = models.ForeignKey(User, related_name='%(class)s_registered_by')
updated_at = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(User, related_name='%(class)s_updated_by')
class Meta:
abstract = True
intro/models/article.py
from django.contrib.auth.models import User
from intro.models.abstract_model import AbstractModel
class Article(AbstractModel):
title = models.CharField(max_length=128)
description = models.CharField(max_length=2048)
author = models.ForeignKey(Author) #La description de la classe Author est omise
categories = models.ForeignKey(Category, null=True, blank=True) #Catégorie Description de la classe omise
url = models.URLField()
intro/models/article_domain.py
from intro.models.article import Article
from intro.consts import DEFAULT_MECAB_PATH
class ArticleDomain(Article):
class Meta:
proxy = True
def __init__(self, *args, **kwargs):
# Do initialize...
def __call__(self, mecab_path=None):
if not mecab_path:
self.mecab_path = DEFAULT_MECAB_PATH
def parse_morpheme(self):
# Do morpheme analysis
@classmethod
def train(cls, filter_params)
authors = filter_params.get('author')
articles = None
if authors and articles:
articles = Articles.objects.filter(authors__in=authors)
# Do some extraction
# Do training
def predict(self, texts)
# Do prediction
Créez un test unitaire avec from django.test import TestCase
lors de la refactorisation
De toute évidence, les perspectives pour le code ont été faussées
Modification de la condition d'extraction de données dans get_queryset dans la classe CustomManager afin que le CustomManager soit conservé dans la classe proxy.
Cela fonctionnait bien avec un modèle de domaine complexe pour les lots.
Ecrire une implémentation d'envoi de courrier électronique dans le modèle ou dessiner solidement urllib2 pour la liaison vers des services externes est gênant.
Création d'une classe Gateway pour la coopération externe (écrite comme adaptateur dans la figure ci-dessus), et hérité et utilisation de la classe Gateway dans la classe de modèle de domaine ou la classe de modèle (écrite comme Application dans la figure ci-dessus). ..
Les classes de modèle utilisent généralement des méthodes de commodité héritées des superclasses. Ensuite, nous avons hérité de la classe Gateway et utilisé des méthodes pratiques.
En ce qui concerne la cause 3, j'ai décidé de renoncer à la gestion et de changer de poste (même si seul le CTO est décent).
Il aurait peut-être été possible d'améliorer l'entreprise si nous avions pris le temps. Cependant, compte tenu du temps qu'il faut aux gens pour changer, de la vitesse à laquelle la technologie progresse et des conditions d'autres entreprises, j'ai pensé que si je restais, je perdrais mon temps et ma vie. Je ne suis pas assez jeune pour tolérer un tel gaspillage. Je ne suis pas assez jeune pour gérer les souvenirs des autres qui sont seuls.
Non seulement pour les startups, mais la direction l'a reconnu comme très important.
Recommended Posts